Banco de Dados Distribuídos - facom.ufu.brilmerio/sbd20132/sbd9_funcoesEgatilhos.pdf · Plpgsql–...

32
GBC043 - Sistemas de Banco de Dados Funções e Gatilhos no PostgreSQL Ilmério Reis da Silva [email protected] www.facom.ufu.br/~ilmerio/sbd UFU/FACOM/BCC

Transcript of Banco de Dados Distribuídos - facom.ufu.brilmerio/sbd20132/sbd9_funcoesEgatilhos.pdf · Plpgsql–...

GBC043 - Sistemas de Banco de DadosFunções e Gatilhos no PostgreSQL

Ilmério Reis da [email protected]/~ilmerio/sbdUFU/FACOM/BCC

Página 2

Prgrama Teórico/Prático - SQL

• Linguagem SQL– Comandos de criação e eliminação de tabelas– Evolução de esquemas de banco de dados– Comandos de inserção de tuplas em tabelas– Comandos de alteração e supressão de tuplas– Comandos de consulta (simples e complexos)– Definição de visões– Funções e gatilhos no servidor de BD

Álgebra Relacional Cálculo Relacional

UFU/FACOM Página 3

SQL EM FUNÇÕES E GATILHOS

Def. Funções e Gatilhos são procedimentos armazenados, interpretados e executados no servidor de BD, escritos em uma linguagem procedural que permite controle de fluxo e cálculos complexos, além de acesso ao BD via SQL.

As funções são chamadas de forma explícita e os gatilhos por meio de eventos, implementando o conceito de “BD Ativo”

UFU/FACOM Página 4

OBJETIVOS DE FUNÇÕES E GATILHOS • padronizar acesso por vários programas;• orientar execuções para o servidor;• controlar acesso via GRANT específico;• adicionar estruturas de controle;• implementar restrições de integridade;• realizar cálculos complexos;

UFU/FACOM Página 5

FUNÇÕES E GATILHOS NO PostgreSQL 1. O SGBD armazena no catálogo comandos em linguagens

procedurais com ou sem instruções SQL;2. Há um interpretador da linguagem;3. O interpretador também é uma função;4. Linguagens: PL/PgSQL, PL/Tcl, PL/Perl, PL/Pynton...5. Devem ser habilitadas pelo super-usuário, por exemplo:

CREATE LANGUAGE plpgsql;6. Outros interpretadores podem ser desenvolvidos pelo

usuário;

UFU/FACOM Página 6

Funções – PlpgSql - Exemplo 0CREATE OR REPLACE FUNCTION

ehoras(employee.ssn%TYPE)RETURNS DECIMAL(6,2) AS '

DECLAREmyssn alias for $1;myhoras DECIMAL(6,2);

BEGINSELECT SUM(hours) FROM works_on INTO myhoras

WHERE essn=myssn;RETURN myhoras;

END;' LANGUAGE 'plpgsql';=> SELECT * FROM ehoras('123456789');

UFU/FACOM Página 7

PlpgSql – Bloco de sentença em Funções

UM BLOCO DE SENTENÇA:

[ << label >> ][ DECLARE

declarações de variáveis ]BEGIN

sentençasEND;

OBS: - um bloco é uma senteça- tudo é convertido para caixa-baixa, exceto se "..."- variáveis de tipos semelhantes ao SQL com peculiaridades...

UFU/FACOM Página 8

Plpgsql – Definindo Variáveisnome [CONSTANT] tipo [NOT NULL] [ { DEFAULT | := } expression ];Exemplos:

user_id INTEGER;quantity NUMERIC(5);url VARCHAR;myrow tablename%ROWTYPE;myfield tablename.fieldname%TYPE;arow RECORD;quantity INTEGER DEFAULT 32;url varchar := ''http://mysite.com'';user_id CONSTANT INTEGER := 10;

OBS: neste caso, myfield é uma variável associada a um atribuo específico e myrow.field é uma variável associada ao atributo field

UFU/FACOM Página 9

Plpgsql – Parâmetros

• $1, $2, etc • Aliases: subtotal ALIAS FOR $1;

UFU/FACOM Página 10

Plpgsql – A variável FOUNDCREATE OR REPLACE FUNCTION

does_employee_exist (employee.ssn%TYPE) RETURNS bool AS 'DECLARE key ALIAS FOR $1; myemployee employee%ROWTYPE;BEGIN

SELECT INTO myemployee * FROM employeeWHERE ssn=key;

IF NOT FOUND THEN RETURN false;END IF;RETURN true;

END;'LANGUAGE 'plpgsql';

UFU/FACOM Página 11

Plpgsql – ESTRUTURAS DE CONTROLE RETURN expression; - - encerra a função RETURN NEXT expression; - - retorna linha e não encerra a funçãoExemplo:

some_func() RETURNS SETOF sometype ...=> SELECT * FROM some_func();

CONDIÇÕESIF ... THEN ... END IF;IF ... THEN ... ELSE ... END IF;IF ... THEN ... ELSE IF ... END IF END IF;IF ... THEN ... ELSIF ... THEN ... ELSE ... END IF;

LAÇOS: WHEN FOR

UFU/FACOM Página 12

Plpgsql – LAÇOS Bloco de sentenças é executado até:

EXIT [ label ] [ WHEN expression ] ou RETURN

[<< label >>]LOOP

sentençasEND LOOP;

UFU/FACOM Página 13

Plpgsql – WHILE

[<< label >>]WHILE expressão

LOOPsentenças

END LOOP;

UFU/FACOM Página 14

Plpgsql – FOR

[<< label >>]FOR nome_var IN [ REVERSE ] exp_from..exp_to LOOP

sentençasEND LOOP;

EXXEMPLO VARRENDO O RESULTADO DE UMA CONSULTA[<< label >>]FOR {record | row} IN select_query

LOOPsentenças

END LOOP;

UFU/FACOM Página 15

Plpgsql – Consultas dinâmicasEXECUTE query-string;

Exemplo:

EXECUTE ''UPDATE tab SET ''|| quote_ident(fieldname) || " = " || quote_literal(newvalue)|| ''WHERE …'';

resultados de consultas podem ser usados em FOR-IN-EXECUTE GET DIAGNOSTICS variable = ROW_COUNT;

UFU/FACOM Página 16

Plpgsql–Varrendo resultado de consulta dinâmica

[<< label >>]

FOR {record | row} IN EXECUTE text exp LOOP

sentençasEND LOOP;

UFU/FACOM Página 17

Plpgsql– IGNORANDO RESULTADOS

PERFORM * FROM works on WHERE essn=my ssn;IF NOT FOUND THEN

RAISE NOTICE ''...''END IF;

UFU/FACOM Página 18

Plpgsql– FUNÇÕES – Exemplo 1CREATE OR REPLACE FUNCTION

tgenero(company.employee.ssn%TYPE)RETURNS TEXT AS '

DECLAREmyssn alias for $1;myrow company.employee%ROWTYPE;mysexo TEXT DEFAULT '' '';

BEGINSELECT * INTO myrow FROM company.employee WHERE ssn=myssn;IF myrow.sex = ''F'' THEN mysexo := ''Feminino'';ELSE IF myrow.sex = ''M'' THEN mysexo := ''Masculino''; END IF;END IF;RETURN myrow.fname || '' '' || myrow.minit || ''. '' || myrow.lname || '' - '' || mysexo;

END;' LANGUAGE 'plpgsql';

SELECT * FROM tgenero('123456789')

UFU/FACOM Página 19

Plpgsql– FUNÇÕES – Exemplo 2 - ProblemaConsidere que um conjunto de comandos contendo junções e/ou funções de agregação têm uma frequência de execução muito alta. Então, por questões de desempenho, vc como projetista do BD optou por criar 'Visões Materializadas' para esses comandos.

Como o PostgreSql não implementa este tipo de visão, vc usará uma tabela gerada porCREATE TABLE table_name AS SELECT ... para cada comando.

Para facilitar a atualização dos dados dessas tabelas, implemente uma função que atualize todas as tabelas. Para isso os nomes e comandos SELECT correspondentes serão armazenados em uma tabela chamada 'visoesmaterializadas' com o seguinte esquema:

CREATE TABLE company.visoesmaterializadas ( mvname varchar(50), -- nome da tabela mvquery varchar(500), -- comando que obtem dados para inserção na tabela primary key (mvname));

UFU/FACOM Página 20

Plpgsql–FUNÇÕES–Exemplo 2–Uma Tabela

CREATE TABLE company.materializeddsummaryAS SELECT dno as Dno, count(*) as NroEmp,sum(salary) as TotalS, avg(salary) as AverageSFROM company.employee GROUP BY dno;

INSERT INTO company.visoesmaterializadas(mvname, mvquery)VALUES ('company.materializeddsummary',

'SELECT dno as Dno, count(*) as NroEmp,sum(salary) as TotalS, avg(salary) as AverageSFROM company.employee GROUP BY dno');

UFU/FACOM Página 21

Plpgsql–FUNÇÕES–Exemplo 2–Outra Tabela

CREATE TABLE company.materializedpsummary1AS SELECT pname AS PName, dname AS DName, COUNT(*) AS NroEmp, SUM(hours) AS SumHours FROM company.project, company.department, company.works_on WHERE pnumber=pno AND dnum=dnumber GROUP BY pname, dname;

INSERT INTO company.visoesmaterializadas(mvname, mvquery)VALUES ('company.materializedpsummary1',

'SELECT pname AS PName, dname AS DName, COUNT(*) AS NroEmp, SUM(hours) AS SumHours FROM company.project, company.department, company.works_on WHERE pnumber=pno AND dnum=dnumber GROUP BY pname, dname');

UFU/FACOM Página 22

Plpgsql– FUNÇÕES – Exemplo 2 – A FunçãoCREATE OR REPLACE FUNCTION atualizavisoes() RETURNS TEXT AS '

DECLAREmviews RECORD;BEGIN

FOR mviews IN SELECT * FROM company.visoesmaterializadasORDER BY mvname LOOP RAISE NOTICE ''Limpando as tuplas de <%> '',

mviews.mvname;EXECUTE ''DELETE FROM '' || mviews.mvname;RAISE NOTICE ''Inserindo versao atualizada de <%> '',

mviews.mvname;EXECUTE ''INSERT INTO ''

|| mviews.mvname || '' '' || mviews.mvquery;END LOOP;RETURN ''As tabelas foram atualizadas!!'';

END;' LANGUAGE 'plpgsql';

SELECT * FROM atualizavisoes()

UFU/FACOM Página 23

Plpgsql– GATILHOSDef. GATILHOS ou TRIGGERS: implementam regras ativasIMPLEMENTAÇÃO Definindo o evento que acionará o gatilho:

CREATE TRIGGER nome gatilho{ BEFORE | AFTER } { evento [OR ...] } ON nome tabelaFOR EACH { ROW | STATEMENT }EXECUTE PROCEDURE nome função ( argumentos )

evento: INSERT, DELETE or UPDATE; FOR EACH: quantas vezes será acionado (só ROW funciona); argumentos: argumentos(literais) passados via TG_ARGV[] Função sem argumento com RETURNS TRIGGER;

UFU/FACOM Página 24

Plpgsql– VARIÁVEIS ESPECIAIS EM GATILHOS VARIÁVEIS ESPECIAIS são criadas automaticamente

– NEW: tipo RECORD contendo nova linha em INSERT/UPDATE– OLD: tipo RECORD contendo linha antiga em UPDATE/DELETE– TG_NAME: o nome do gatilho– TG_WHEN: BEFORE or AFTER– TG_LEVEL: ROW or STATEMENT– TG_OP: INSERT, UPDATE or DELETE– TG_RELID: object ID da tabela– TG_RELNAME: nome da tabela– TG_NARGS: n´umero de argumentos– TG_ARGV[]: array contendo argumento

UFU/FACOM Página 25

Plpgsql– RETORNOS EM GATILHOS RETORNO: deve ser NULL ou um RECORD/ROW

– NULL em BEFORE salta o restante das opera¸c˜oes– RECORD/ROW em BEFORE prossegue com o valor retornado– Em AFTER o valor retornado é ignorado

UFU/FACOM Página 26

Plpgsql– Gatilho – EXEMPLO 0- Salário ok?CREATE FUNCTION emp_stamp () RETURNS TRIGGER AS 'BEGIN

IF NEW.salary ISNULL THENRAISE EXCEPTION '' % : Informe Salario'',

NEW.fname, NEW.lname;END IF;IF NEW.salary <= 0 THEN

RAISE EXCEPTION ''% % : Informe Salario > 0'',NEW.fname, NEW.lname;

END IF;RETURN NEW; – corretoEND;' LANGUAGE 'plpgsql';CREATE TRIGGER emp stamp

BEFORE INSERT OR UPDATE ON employeeFOR EACH ROW EXECUTE PROCEDURE emp_stamp();

UFU/FACOM Página 27

Plpgsql–Gatilho – EXEMPLO 1- Uma esposaCREATE OR REPLACE FUNCTION spouse () RETURNS TRIGGER AS ' BEGIN

IF NEW.relationship = ''SPOUSE'' THENPERFORM * FROM dependent WHERE essn=NEW.essn

AND dependent_name <> NEW.dependent_name AND relationship=''SPOUSE'';

IF FOUND THEN RAISE EXCEPTION ''Employee % still have a spouse'',NEW.essn;

END IF;END IF;

RETURN NEW; END;' LANGUAGE 'plpgsql';CREATE TRIGGER spouse BEFORE INSERT OR UPDATE ON dependent FOR EACH ROW EXECUTE PROCEDURE spouse();

UFU/FACOM Página 28

Plpgsql–Gatilho – EXEMPLO 2- dsummary1CREATE OR REPLACE FUNCTION fnewemp () RETURNS TRIGGER AS ' DECLARE

mycomand TEXT; mydsummary materializeddsummary%ROWTYPE;mynroemp materializeddsummary.nroemp%TYPE;

mytotals materializeddsummary.totals%TYPE; myaverages materializeddsummary.averages%TYPE; BEGIN IF NEW.dno ISNULL THEN RAISE EXCEPTION ''dno cannot be NULL value''; END IF; IF NEW.salary ISNULL THEN RAISE EXCEPTION ''% cannot have NULL salary'',

NEW.fname; END IF; IF NEW.salary <= 0 THEN RAISE EXCEPTION ''% cannot have a negative salary'',

NEW.fname; END IF; - - continua

UFU/FACOM Página 29

Plpgsql–Gatilho – EXEMPLO 2- dsummary1 - - continuação

SELECT * FROM materializeddsummary INTO mydsummary WHERE dno=NEW.dno;mynroemp = mydsummary.nroemp + 1;mytotals = mydsummary.totals + NEW.salary;myaverages = ((mydsummary.averages * mydsummary.nroemp) + NEW.salary) /

mynroemp;mycomand := ''UPDATE '' || ''materializeddsummary '' || ''SET nroemp = ''

|| quote_literal(mynroemp) || '', totals = '' || quote_literal(mytotals) || '', averages = '' || quote_literal(myaverages) || '' '' || ''WHERE dno = '' || quote_literal(NEW.dno);

EXECUTE mycomand;RETURN NEW;

END;' LANGUAGE 'plpgsql';CREATE TRIGGER tnewemp BEFORE INSERT ON employee FOR EACH ROW EXECUTE PROCEDURE fnewemp();

UFU/FACOM Página 30

PlpgsqlEXERCÍCIOS DE IMPLEMENTAÇÃO

=> Lab– Implementar os gatilhos que atualizam as tabelas

anteriores para os comandos UPDATE e DELETE, mantendo as “visões” atualizadas

– Verificar a execução automática das funções quando os comandos INSERT, UPDATE e DELETE em employee forem executados

– Ver exercícios de funções e gatilhos para o BD SAFIS

UFU/FACOM Página 31

Bibliografia• [EN] Capítulos 4,5,26• [SK] Capítulos • [RG] Capítulos• Manuais do PostgreSQL

UFU/FACOM Página 32

Plpgsql

FIM – Plpgsql