View
218
Download
0
Category
Preview:
Citation preview
GBC043 - Sistemas de Banco de DadosFunções e Gatilhos no PostgreSQL
Ilmério Reis da Silvailmerio@facom.ufu.brwww.facom.ufu.br/~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 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 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
Recommended