Aplicativos em PHP/Administração dos SGBDs/PostgreSQL
Aqui apenas passo o link para o livro
PostgreSQL Prático
Com isso evitando repetições ou a reinvenção da roda.
Tutorial sobre o phpPgAdmin
- Baixar de - http://phppgadmin.sourceforge.net/
- Copiar para o diretório web e descompactar no diretório "phppgadmin"
- Editar o arquivo conf/config.inc.php e alterar para dois servidores (um local e outro remoto) caso assim queira:
//...
// Servidor Local
// Display name for the server on the login screen
$conf['servers'][0]['desc'] = 'Local';
// Hostname or IP address for server. Use '' for UNIX domain socket.
// use 'localhost' for TCP/IP connection on this computer
$conf['servers'][0]['host'] = '127.0.0.1';
$conf['servers'][0]['defaultdb'] = 'nomebancodefault';
//...
// Servidor Remoto
// Example for a second server
$conf['servers'][1]['desc'] = 'Remoto';
$conf['servers'][1]['host'] = '10.99.00.11';
$conf['servers'][1]['port'] = 5432;
$conf['servers'][1]['defaultdb'] = 'nomebancodefault';
//...
// If extra login security is true, then logins via phpPgAdmin with no
// password or certain usernames (pgsql, postgres, root, administrator)
// will be denied. Only set this false once you have read the FAQ and
// understand how to change PostgreSQL's pg_hba.conf to enable
// passworded local connections.
$conf['extra_login_security'] = false;
// Importantes para Importar/Exportar
$conf['servers'][0]['pg_dump_path'] = '/usr/local/pgsql/bin/pg_dump';
$conf['servers'][0]['pg_dumpall_path'] = '/usr/local/pgsql/bin/pg_dumpall';
- Pronto, basta abrir http://localhost/phppgadmin
Criando Chave Estrangeira Composta no phpPgAdmin e no PGAdmin
O exemplo é para uma chave com 4 campos, mas o procedimento é válido para qualquer chave composta.
Relacionar composicoes com precos.
Quero que ao digitar um preço em composições o SGBD verifique a integridade do mesmo em precos.
Temos uma tabela cuja chave primária é composta por 4 campos:
tabela, insumo_grupo, insumo, fornecedor e queremos relacionar com outra tabela que também contém os mesmos campos com os mesmos tipos de dados.
Como isso pode ter algum engano, seguem os passos.
Esquema das tabelas:
CREATE TABLE "composicoes" (
"servico" character varying(10) NOT NULL,
"insumo_grupo" character varying(6) NOT NULL,
"insumo" character varying(10) NOT NULL,
"quantidade" real NOT NULL DEFAULT 0,
"coeficiente" real NOT NULL DEFAULT 0,
"data_inclusao" timestamp without time zone,
"data_alteracao" timestamp without time zone,
"data_cancelamento" timestamp without time zone,
"uid_inclusao" character varying(10),
"uid_alteracao" character varying(10),
"uid_cancelamento" character varying(10),
"tabela" smallint NOT NULL DEFAULT 2006,
"fornecedor" smallint NOT NULL DEFAULT 1,
CONSTRAINT "composicoes_fk_insumo" FOREIGN KEY (insumo_grupo, insumo) REFERENCES insumos(grupo, insumo),
CONSTRAINT "composicoes_fk_servicos" FOREIGN KEY (servico) REFERENCES servicos(servico),
CONSTRAINT "composicoes_pkey" PRIMARY KEY (servico, insumo_grupo, insumo)
) WITH OIDS;
CREATE TABLE "precos" (
"tabela" smallint NOT NULL,
"insumo_grupo" character varying(6) NOT NULL,
"insumo" character varying(10) NOT NULL,
"fornecedor" smallint NOT NULL,
"custo_produtivo" real,
"custo_improdutivo" real,
"data_coleta" timestamp without time zone,
"data_inclusao" timestamp without time zone,
"data_alteracao" timestamp without time zone,
"data_cancelamento" timestamp without time zone,
"uid_inclusao" character varying(10),
"uid_alteracao" character varying(10),
"uid_cancelamento" character varying(10),
CONSTRAINT "precos_fornecedor_fk" FOREIGN KEY (fornecedor) REFERENCES cadastro.fornecedor(codigo_fornecedor) ON UPDATE RESTRICT ON DELETE RESTRICT,
CONSTRAINT "precos_insumos_fk" FOREIGN KEY (insumo_grupo, insumo) REFERENCES insumos(grupo, insumo),
CONSTRAINT "precos_pk" PRIMARY KEY (tabela, insumo_grupo, insumo, fornecedor)
) WITH OIDS;
phpPgAdmin
Observe que na tabela de destino (precos), de onde queremos trazer uma informação, a ordem da chave é "tabela, insumo_grupo, insumo e fornecedor". Já em coposicoes, estes campos estão em ordem diferente. Mas no momento de criar a chave estrangeira devemos adicionar na mesma ordem da precos. Veja abaixo.
- Abrir a tabela (composicoes) para a qual queremos inserir uma chave estrangeira no phpPgAdmin
- Clicar na aba Restrição
- Clicar em adicionar chave estrangeira
- Digitar um nome para a chave: composicoes_precos_fk
- Agora vem a dica: selecione nesta ordem à esquerda e clique na seta dupla após selecionar cada um:
- primeiro tabela
- depois insumo_grupo
- depois insumo
- por fim fornecedor (caso altere esta ordem a chave não será criada)
- Em Tabela alvo selecione preco
- Clique em Adicionar (abaixo)
- Á esquerda selecione os quatro campos de precos (tabela, insumo_grupo, insumo e fornecedor) e clique na seta dupla para a direita.
- Clique em Adicionar abaixo.
PGAdmin
- Abrir a tabela no PgAdmin (duplo clique no nome da tabela à esquerda)
- Clique em Constraints
- Selecione Fireign Key abaixo e clique em Add
- Digite um nome em name "composicoes_precos_fk"
- Em references selecione "precos"
- Clique na aba Columns
- Em Local column selecione 'tabela'
- Em Referecing selecione 'tabela' e clique em Add
- De forma semelhante adicione os 3 pares restantes: insumo_grupo - insumo_grupo, insumo - insumo e fornecedor - fornecedor.
Tive problema ao adicionar insumo, então adicionei fornecedor antes e depois insumo e foi bem.
- Então clique em OK, confira e OK novamente.
PlPgSQL Tutorial
Esta é uma das linguagens de funções armazenadas com que trabalha o PostgreSQL. É a mais popular das linguagens no PostgreSQL.
Delimitadores
O código de uma função plpgsql é especificado em CREATE FUNCTION como uma string literal delimitado por aspas.
1 Apóstrofo (corpo da função)
CREATE FUNCTION olamundo() RETURNS integer AS '
....
' LANGUAGE plpgsql;
Inicia após AS e termina antes de LANGUAGE
2 Apóstrofos (mascar string literal no corpo da função)
a_output := Blah;
SELECT * FROM users WHERE f_name=foobar;
4 Apóstrofos (string constante no corpo da função)
a_output := a_output || AND name LIKE 'foobar' AND xyz
Mais detalhes na documentação oficial.
Declaração de Variáveis
user_id integer; quantity numeric(5); url varchar; myrow tablename%ROWTYPE; myfield tablename.columnname%TYPE; arow RECORD;
Sintaxe geral da declaração de variáveis:
nome [ CONSTANT ] tipo [ NOT NULL ] [ { DEFAULT | := } expressão ];
Exemplos:
quantity integer DEFAULT 32; url varchar := 'http://mysite.com'; user_id CONSTANT integer := 10;
Declarado Funções
CREATE FUNCTION func_escopo() RETURNS integer AS $$
DECLARE
quantidade integer := 30;
BEGIN
RAISE NOTICE 'Aqui a quantidade é %', quantidade; -- A quantidade aqui é 30
quantidade := 50;
--
-- Criar um sub-bloco
--
DECLARE
quantidade integer := 80;
BEGIN
RAISE NOTICE 'Aqui a quantidade é %', quantidade; -- A quantidade aqui é 80
END;
RAISE NOTICE 'Aqui a quantidade é %', quantidade; -- A quantidade aqui é 50
RETURN quantidade;
END;
$$ LANGUAGE plpgsql;
Execute
SELECT func_escopo() as escopo;
Obs.: O psql exibe as mensagens disparadas pelo RAISE.
Obs2.: BEGIN e END na plpgsql não agrupa transações, apenas grupos de comandos.
Alias para Parâmetros de Funções
Sintaxe:
nome ALIAS FOR $n;
Exemplos:
CREATE FUNCTION vendas_taxa(real) RETURNS real AS $$
DECLARE
subtotal ALIAS FOR $1;
BEGIN
RETURN subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;
$1 é um alias para o parâmetro da função que é do tipo real.
CREATE FUNCTION instr(varchar, integer) RETURNS integer AS $$
DECLARE
v_string ALIAS FOR $1;
index ALIAS FOR $2;
BEGIN
-- Aqui fazemos alguns cálculos usando v_string e index
END;
$$ LANGUAGE plpgsql;
CREATE FUNCTION concat_campos(tab tbl_clientes) RETURNS text AS $$
BEGIN
RETURN tab.nome || '' '' || tab.email || '' '' || tab.cidade || '' '' || tab.estado;
END;
$$ LANGUAGE plpgsql;
Também podemos (mais claro) usar os nomes dos parâmetros explicitamente:
CREATE FUNCTION sales_tax(subtotal real, OUT tax real) AS $$
BEGIN
tax := subtotal * 0.06;
END;
$$ LANGUAGE plpgsql;
O parâmetro OUT (output, saída) é mais útil quando retorna múltiplos parâmetros:
CREATE FUNCTION sum_n_product(x int, y int, OUT sum int, OUT prod int) AS $$
BEGIN
sum := x + y;
prod := x * y;
END;
$$ LANGUAGE plpgsql
Executando Funções
Usa-se o select para executar uma função, como se fosse uma view ou consulta comum.
select sum_n_product(5, 6);
Tipos Polimórficos
Quanto tipos polimórficos (anyelement e anyarray) são usados para declarar de funções, um parâmetro especial ($0) é criado. Este tipo de dados é o atual retorno da função.
É inicializado como NULL e pode ser modificado pela função.
Exemplo
Função que trabalha com qualquer tipo de dados e que suporta o operador +:
CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement)
RETURNS anyelement AS $$
DECLARE
result ALIAS FOR $0;
BEGIN
result := v1 + v2 + v3;
RETURN result;
END;
$$ LANGUAGE plpgsql;
Ou:
CREATE FUNCTION add_three_values(v1 anyelement, v2 anyelement, v3 anyelement,
OUT sum anyelement)
AS $$
BEGIN
sum := v1 + v2 + v3;
END;
$$ LANGUAGE plpgsql;
Veja, que ao usar OUT não há necessidade de retorno (já está implícito).
Copiando Tipos
variavel%TYPE
%TYPE fornece o tipo de dados de uma variável ou de um campo de tabela.
Para declarar uma variável com o mesmo tipo de dado de usuarios.id_usuario deve ser escrito:
id_usuario usuarios.id_usuario%TYPE;
Tipos row
Pode armazenar um registro resultante de um SELECT ou de um FOR.
nome nome_da_tabela%ROWTYPE;
nome nome_do_tipo_composto;
Os campos podem ser acessados com nomevariavel.nomecampo;
Exemplo de uso:
CREATE FUNCTION mesclar_campos(t_linha nome_da_tabela) RETURNS text AS $$
DECLARE
t2_linha nome_tabela2%ROWTYPE;
BEGIN
SELECT * INTO t2_linha FROM nome_tabela2 WHERE ... ;
RETURN t_linha.f1 || t2_linha.f3 || t_linha.f5 || t2_linha.f7;
END;
$$ LANGUAGE plpgsql;
SELECT mesclar_campos(t.*) FROM nome_da_tabela t WHERE ... ;
Tipo registro (record)
nome record;
Renomeando Variáveis
RENAME nome_antigo TO novo_nome;
CREATE FUNCTION logfunc1(logtxt text) RETURNS timestamp AS $$
BEGIN
INSERT INTO logtable VALUES (logtxt, 'now');
RETURN 'now';
END;
$$ LANGUAGE plpgsql;
e
CREATE FUNCTION logfunc2(logtxt text) RETURNS timestamp AS $$
DECLARE
curtime timestamp;
BEGIN
curtime := 'now';
INSERT INTO logtable VALUES (logtxt, curtime);
RETURN curtime;
END;
$$ LANGUAGE plpgsql;
Atribuições
identificador := expressão;
SELECT INTO meu_registro * FROM emp WHERE nome_emp = meu_nome;
IF NOT FOUND THEN
RAISE EXCEPTION ''não foi encontrado o empregado %!'', meu_nome;
END IF;
Execução de Expressão ou Consulta sem Resultado
PERFORM create_mv('cs_session_page_requests_mv', my_query);
Não Fazer Nada
NULL;
Por exemplo, os dois fragmentos de código a seguir são equivalentes:
BEGIN
y := x / 0;
EXCEPTION
WHEN division_by_zero THEN
NULL; -- ignorar o erro
END;
BEGIN
y := x / 0;
EXCEPTION
WHEN division_by_zero THEN -- ignorar o erro
END;
Execução de Comandos Dinâmicos
EXECUTE cadeia_de_caracteres_do_comando;
EXECUTE 'UPDATE tbl SET '
|| quote_ident(nome_da_coluna)
|| ' = '
|| quote_literal(novo_valor)
|| ' WHERE key = '
|| quote_literal(valor_chave);
EXECUTE 'UPDATE tbl SET '
|| quote_ident(nome_da_coluna)
|| ' = $$'
|| novo_valor
|| '$$ WHERE key = '
|| quote_literal(valor_chave);
Estruturas de Controle
Return next
RETURN NEXT expressão;
Quando uma função PL/pgSQL é declarada como retornando SETOF algum_tipo, o procedimento a ser seguido é um pouco diferente. Neste caso, os itens individuais a serem retornados são especificados em comandos RETURN NEXT, e um comando RETURN final, sem nenhum argumento, é utilizado para indicar que a função chegou ao fim de sua execução. O comando RETURN NEXT pode ser utilizado tanto com tipos de dado escalares quanto compostos; no último caso toda uma “tabela” de resultados é retornada.
As funções que utilizam RETURN NEXT devem ser chamadas da seguinte maneira:
SELECT * FROM alguma_função();
Condicionais
• IF ... THEN • IF ... THEN ... ELSE • IF ... THEN ... ELSE IF • IF ... THEN ... ELSIF ... THEN ... ELSE • IF ... THEN ... ELSEIF ... THEN ... ELSE IF linha_demo.sexo = 'm' THEN sexo_extenso := 'masculino'; ELSE IF linha_demo.sexo = 'f' THEN sexo_extenso := 'feminino'; END IF; END IF; IF expressão_booleana THEN instruções [ ELSIF expressão_booleana THEN instruções [ ELSIF expressão_booleana THEN instruções ...]] [ ELSE instruções ] END IF; IF numero = 0 THEN resultado := 'zero'; ELSIF numero > 0 THEN resultado := 'positivo'; ELSIF numero < 0 THEN resultado := 'negativo'; ELSE -- hmm, a única outra possibilidade é que o número seja nulo resultado := 'NULL'; END IF; Laços [<<rótulo>>] LOOP instruções END LOOP; Exit EXIT [ rótulo ] [ WHEN expressão ]; LOOP -- algum processamento IF contador > 0 THEN EXIT; -- sair do laço END IF; END LOOP; LOOP -- algum processamento EXIT WHEN contador > 0; -- mesmo resultado do exemplo acima END LOOP; BEGIN -- algum processamento IF estoque > 100000 THEN EXIT; -- causa a saída do bloco BEGIN END IF; END; While [<<rótulo>>] WHILE expressão LOOP instruções END LOOP; WHILE quantia_devida > 0 AND saldo_do_certificado_de_bonus > 0 LOOP -- algum processamento END LOOP; WHILE NOT expressão_booleana LOOP -- algum processamento END LOOP; For (laços internos) [<<rótulo>>] FOR nome IN [ REVERSE ] expressão .. expressão LOOP instruções END LOOP; FOR i IN 1..10 LOOP -- algum processamento RAISE NOTICE 'i é %', i; END LOOP; FOR i IN REVERSE 10..1 LOOP -- algum processamento END LOOP;
Laços através do resultado de consultas
[<<rótulo>>] FOR registro_ou_linha IN comando LOOP instruções END LOOP; CREATE FUNCTION cs_refresh_mviews() RETURNS integer AS $$ DECLARE mviews RECORD; BEGIN PERFORM cs_log('Atualização das visões materializadas...'); FOR mviews IN SELECT * FROM cs_materialized_views ORDER BY sort_key LOOP -- Agora "mviews" possui um registro de cs_materialized_views PERFORM cs_log('Atualizando a visão materializada ' || quote_ident(mviews.mv_name) || ' ...'); EXECUTE 'TRUNCATE TABLE ' || quote_ident(mviews.mv_name); EXECUTE 'INSERT INTO ' || quote_ident(mviews.mv_name) || ' ' || mviews.mv_query; END LOOP; PERFORM cs_log('Fim da atualização das visões materializadas.'); RETURN 1; END; $$ LANGUAGE plpgsql; [<<rótulo>>] FOR registro_ou_linha IN EXECUTE texto_da_expressão LOOP instruções END LOOP;
Capturar Erros
[ <<rótulo>> ] [ DECLARE declarações ] BEGIN instruções EXCEPTION WHEN condição [ OR condição ... ] THEN instruções_do_tratador [ WHEN condição [ OR condição ... ] THEN instruções_do_tratador ... ] END; INSERT INTO minha_tabela(nome, sobrenome) VALUES('Tom', 'Jones'); BEGIN UPDATE minha_tabela SET nome = 'Joe' WHERE sobrenome = 'Jones'; x := x + 1; y := x / 0; EXCEPTION WHEN division_by_zero THEN RAISE NOTICE 'capturado division_by_zero'; RETURN x; END;
Declaração de Variáveis do tipo Cursor
Todos os acessos aos cursores na linguagem PL/pgSQL são feitos através de variáveis cursor, que sempre são do tipo de dado especial refcursor. Uma forma de criar uma variável cursor é simplesmente declará-la como sendo do tipo refcursor. Outra forma é utilizar a sintaxe de declaração de cursor, cuja forma geral é:
nome CURSOR [ ( argumentos ) ] FOR comando ;
Exemplos:
DECLARE curs1 refcursor; curs2 CURSOR FOR SELECT * FROM tenk1; curs3 CURSOR (chave integer) IS SELECT * FROM tenk1 WHERE unico1 = chave; CREATE TABLE teste (col text); INSERT INTO teste VALUES ('123'); CREATE FUNCTION reffunc(refcursor) RETURNS refcursor AS ' BEGIN OPEN $1 FOR SELECT col FROM teste; RETURN $1; END; ' LANGUAGE plpgsql; BEGIN; SELECT reffunc('funccursor'); reffunc ------------ funccursor (1 linha) FETCH ALL IN funccursor; COMMIT;
Erros e Mensagens
RAISE nível 'formato' [, variável [, ...]];
Os níveis possíveis são DEBUG, LOG, INFO, NOTICE, WARNING, e EXCEPTION. O nível EXCEPTION causa um erro (que normalmente interrompe a transação corrente); os outros níveis apenas geram mensagens com diferentes níveis de prioridade. Se as mensagens de uma determinada prioridade são informadas ao cliente, escritas no log do servidor, ou as duas coisas, é controlado pelas variáveis de configuração log_min_messages e client_min_messages.
Procedimentos de Gatilho (Trigger)
É criado pelo comando CREATE FUNCTION, declarando o procedimento como uma função sem argumentos e que retorna o tipo trigger. Deve ser observado que a função deve ser declarada sem argumentos, mesmo que espere receber os argumentos especificados no comando CREATE TRIGGER — os argumentos do gatilho são passados através de TG_ARGV, conforme descrito abaixo.
Quando uma função escrita em PL/pgSQL é chamada como um gatilho, diversas variáveis especiais são criadas automaticamente no bloco de nível mais alto. São estas:
NEW
Tipo de dado RECORD; variável contendo a nova linha do banco de dados, para as operações de INSERT/UPDATE nos gatilhos no nível de linha. O valor desta variável é NULL nos gatilhos no nível de instrução.
OLD
Tipo de dado RECORD; variável contendo a antiga linha do banco de dados, para as operações de UPDATE/DELETE nos gatilhos no nível de linha. O valor desta variável é NULL nos gatilhos no nível de instrução.
TG_NAME
Tipo de dado name; variável contendo o nome do gatilho disparado.
TG_WHEN
Tipo de dado text; uma cadeia de caracteres contendo BEFORE ou AFTER, dependendo da definição do gatilho.
TG_LEVEL
Tipo de dado text; uma cadeia de caracteres contendo ROW ou STATEMENT, dependendo da definição do gatilho.
TG_OP
Tipo de dado text; uma cadeia de caracteres contendo INSERT, UPDATE, ou DELETE, informando para qual operação o gatilho foi disparado.
TG_RELID
Tipo de dado oid; o ID de objeto da tabela que causou o disparo do gatilho.
TG_RELNAME
Tipo de dado name; o nome da tabela que causou o disparo do gatilho.
TG_NARGS
Tipo de dado integer; o número de argumentos fornecidos ao procedimento de gatilho na instrução CREATE
TG_ARGV[]
Tipo de dado matriz de text; os argumentos da instrução CREATE TRIGGER. O contador do índice começa por 0.
Índices inválidos (menor que 0 ou maior ou igual a tg_nargs) resultam em um valor nulo.
Exemplo:
O gatilho deste exemplo garante que:
- quando é inserida ou atualizada uma linha na tabela, fica sempre registrado nesta linha o usuário que efetuou a inserção ou a atualização
- quando isto ocorreu.
- além disso, o gatilho verifica se é fornecido o nome do empregado
- e se o valor do salário é um número positivo.
CREATE TABLE emp ( nome_emp text, salario integer, ultima_data timestamp, ultimo_usuario text ); CREATE FUNCTION emp_gatilho() RETURNS trigger AS $emp_gatilho$ BEGIN -- Verificar se foi fornecido o nome e o salário do empregado IF NEW.nome_emp IS NULL THEN RAISE EXCEPTION 'O nome do empregado não pode ser nulo'; END IF; IF NEW.salario IS NULL THEN RAISE EXCEPTION '% não pode ter um salário nulo', NEW.nome_emp; END IF; -- Quem paga para trabalhar? IF NEW.salario < 0 THEN RAISE EXCEPTION '% não pode ter um salário negativo', NEW.nome_emp; END IF; -- Registrar quem alterou a folha de pagamento e quando NEW.ultima_data := 'now'; NEW.ultimo_usuario := current_user; RETURN NEW; END; $emp_gatilho$ LANGUAGE plpgsql; CREATE TRIGGER emp_gatilho BEFORE INSERT OR UPDATE ON emp FOR EACH ROW EXECUTE PROCEDURE emp_gatilho(); INSERT INTO emp (nome_emp, salario) VALUES ('João',1000); INSERT INTO emp (nome_emp, salario) VALUES ('José',1500); INSERT INTO emp (nome_emp, salario) VALUES ('Maria',2500); SELECT * FROM emp;
Gatilho para registrar inserções e atualizações
CREATE TABLE emp ( nome_emp text, salario integer, usu_cria text, -- Usuário que criou a linha data_cria timestamp, -- Data da criação da linha usu_atu text, -- Usuário que fez a atualização data_atu timestamp -- Data da atualização ); CREATE FUNCTION emp_gatilho() RETURNS trigger AS $emp_gatilho$ BEGIN -- Verificar se foi fornecido o nome do empregado IF NEW.nome_emp IS NULL THEN RAISE EXCEPTION 'O nome do empregado não pode ser nulo'; END IF; IF NEW.salario IS NULL THEN RAISE EXCEPTION '% não pode ter um salário nulo', NEW.nome_emp; END IF; -- Quem paga para trabalhar? IF NEW.salario < 0 THEN RAISE EXCEPTION '% não pode ter um salário negativo', NEW.nome_emp; END IF; -- Registrar quem criou a linha e quando IF (TG_OP = 'INSERT') THEN NEW.data_cria := current_timestamp; NEW.usu_cria := current_user; -- Registrar quem alterou a linha e quando ELSIF (TG_OP = 'UPDATE') THEN NEW.data_atu := current_timestamp; NEW.usu_atu := current_user; END IF; RETURN NEW; END; $emp_gatilho$ LANGUAGE plpgsql; CREATE TRIGGER emp_gatilho BEFORE INSERT OR UPDATE ON emp FOR EACH ROW EXECUTE PROCEDURE emp_gatilho(); INSERT INTO emp (nome_emp, salario) VALUES ('João',1000); INSERT INTO emp (nome_emp, salario) VALUES ('José',1500); INSERT INTO emp (nome_emp, salario) VALUES ('Maria',250); UPDATE emp SET salario = 2500 WHERE nome_emp = 'Maria'; SELECT * FROM emp;
Gatilho para auditoria
Todas as operações na tabela emp serão registradas na tabela emp_audit
CREATE TABLE emp ( nome_emp text NOT NULL, salario integer ); CREATE TABLE emp_audit( operacao char(1) NOT NULL, usuario text NOT NULL, data timestamp NOT NULL, nome_emp text NOT NULL, salario integer ); CREATE OR REPLACE FUNCTION processa_emp_audit() RETURNS TRIGGER AS $emp_audit$ BEGIN -- -- Cria uma linha na tabela emp_audit para refletir a operação -- realizada na tabela emp. Utiliza a variável especial TG_OP -- para descobrir a operação sendo realizada. -- IF (TG_OP = 'DELETE') THEN INSERT INTO emp_audit SELECT 'E', user, now(), OLD.*; RETURN OLD; ELSIF (TG_OP = 'UPDATE') THEN INSERT INTO emp_audit SELECT 'A', user, now(), NEW.*; RETURN NEW; ELSIF (TG_OP = 'INSERT') THEN INSERT INTO emp_audit SELECT 'I', user, now(), NEW.*; RETURN NEW; END IF; RETURN NULL; -- o resultado é ignorado uma vez que este é um gatilho AFTER END; $emp_audit$ language plpgsql; CREATE TRIGGER emp_audit AFTER INSERT OR UPDATE OR DELETE ON emp FOR EACH ROW EXECUTE PROCEDURE processa_emp_audit(); INSERT INTO emp (nome_emp, salario) VALUES ('João',1000); INSERT INTO emp (nome_emp, salario) VALUES ('José',1500); INSERT INTO emp (nome_emp, salario) VALUES ('Maria',250); UPDATE emp SET salario = 2500 WHERE nome_emp = 'Maria'; DELETE FROM emp WHERE nome_emp = 'João'; SELECT * FROM emp; SELECT * FROM emp_audit;
Autoditoria ao nível de campos
CREATE TABLE emp ( id serial PRIMARY KEY, nome_emp text NOT NULL, salario integer ); CREATE TABLE emp_audit( usuario text NOT NULL, data timestamp NOT NULL, id integer NOT NULL, coluna text NOT NULL, valor_antigo text NOT NULL, valor_novo text NOT NULL ); CREATE OR REPLACE FUNCTION processa_emp_audit() RETURNS TRIGGER AS $emp_audit$ BEGIN -- -- Não permitir atualizar a chave primária -- IF (NEW.id <> OLD.id) THEN RAISE EXCEPTION 'Não é permitido atualizar o campo ID'; END IF; -- -- Inserir linhas na tabela emp_audit para refletir as alterações -- realizada na tabela emp. -- IF (NEW.nome_emp <> OLD.nome_emp) THEN INSERT INTO emp_audit SELECT current_user, current_timestamp, NEW.id, 'nome_emp', OLD.nome_emp, NEW.nome_emp; END IF; IF (NEW.salario <> OLD.salario) THEN INSERT INTO emp_audit SELECT current_user, current_timestamp, NEW.id, 'salario', OLD.salario, NEW.salario; END IF; RETURN NULL; -- o resultado é ignorado uma vez que este é um gatilho AFTER END; $emp_audit$ language plpgsql; CREATE TRIGGER emp_audit AFTER UPDATE ON emp FOR EACH ROW EXECUTE PROCEDURE processa_emp_audit(); INSERT INTO emp (nome_emp, salario) VALUES ('João',1000); INSERT INTO emp (nome_emp, salario) VALUES ('José',1500); INSERT INTO emp (nome_emp, salario) VALUES ('Maria',2500); UPDATE emp SET salario = 2500 WHERE id = 2; UPDATE emp SET nome_emp = 'Maria Cecília' WHERE id = 3; UPDATE emp SET id=100 WHERE id=1; ERRO: Não é permitido atualizar o campo ID SELECT * FROM emp; SELECT * FROM emp_audit;
Gatilho para manter uma tabela sumário
O esquema que está detalhado a seguir é parcialmente baseado no exemplo Grocery Store do livro The Data Warehouse Toolkit de Ralph Kimball.
-- -- Main tables - time dimension and sales fact. -- CREATE TABLE time_dimension ( time_key integer NOT NULL, day_of_week integer NOT NULL, day_of_month integer NOT NULL, month integer NOT NULL, quarter integer NOT NULL, year integer NOT NULL ); CREATE UNIQUE INDEX time_dimension_key ON time_dimension(time_key); CREATE TABLE sales_fact ( time_key integer NOT NULL, product_key integer NOT NULL, store_key integer NOT NULL, amount_sold numeric(12,2) NOT NULL, units_sold integer NOT NULL, amount_cost numeric(12,2) NOT NULL ); CREATE INDEX sales_fact_time ON sales_fact(time_key); -- -- Summary table - sales by time. -- CREATE TABLE sales_summary_bytime ( time_key integer NOT NULL, amount_sold numeric(15,2) NOT NULL, units_sold numeric(12) NOT NULL, amount_cost numeric(15,2) NOT NULL ); CREATE UNIQUE INDEX sales_summary_bytime_key ON sales_summary_bytime(time_key); -- -- Function and trigger to amend summarized column(s) on UPDATE, INSERT, DELETE. -- CREATE OR REPLACE FUNCTION maint_sales_summary_bytime() RETURNS TRIGGER AS $maint_sales_summary_bytime$ DECLARE delta_time_key integer; delta_amount_sold numeric(15,2); delta_units_sold numeric(12); delta_amount_cost numeric(15,2); BEGIN -- Work out the increment/decrement amount(s). IF (TG_OP = 'DELETE') THEN delta_time_key = OLD.time_key; delta_amount_sold = -1 * OLD.amount_sold; delta_units_sold = -1 * OLD.units_sold; delta_amount_cost = -1 * OLD.amount_cost; ELSIF (TG_OP = 'UPDATE') THEN -- forbid updates that change the time_key - -- (probably not too onerous, as DELETE + INSERT is how most -- changes will be made). IF ( OLD.time_key != NEW.time_key) THEN RAISE EXCEPTION 'Update of time_key : % -> % not allowed', OLD.time_key, NEW.time_key; END IF; delta_time_key = OLD.time_key; delta_amount_sold = NEW.amount_sold - OLD.amount_sold; delta_units_sold = NEW.units_sold - OLD.units_sold; delta_amount_cost = NEW.amount_cost - OLD.amount_cost; ELSIF (TG_OP = 'INSERT') THEN delta_time_key = NEW.time_key; delta_amount_sold = NEW.amount_sold; delta_units_sold = NEW.units_sold; delta_amount_cost = NEW.amount_cost; END IF; -- Update the summary row with the new values. UPDATE sales_summary_bytime SET amount_sold = amount_sold + delta_amount_sold, units_sold = units_sold + delta_units_sold, amount_cost = amount_cost + delta_amount_cost WHERE time_key = delta_time_key; -- There might have been no row with this time_key (e.g new data!). IF (NOT FOUND) THEN BEGIN INSERT INTO sales_summary_bytime ( time_key, amount_sold, units_sold, amount_cost) VALUES ( delta_time_key, delta_amount_sold, delta_units_sold, delta_amount_cost ); EXCEPTION -- -- Catch race condition when two transactions are adding data -- for a new time_key. -- WHEN UNIQUE_VIOLATION THEN UPDATE sales_summary_bytime SET amount_sold = amount_sold + delta_amount_sold, units_sold = units_sold + delta_units_sold, amount_cost = amount_cost + delta_amount_cost WHERE time_key = delta_time_key; END; END IF; RETURN NULL; END; $maint_sales_summary_bytime$ LANGUAGE plpgsql; CREATE TRIGGER maint_sales_summary_bytime AFTER INSERT OR UPDATE OR DELETE ON sales_fact FOR EACH ROW EXECUTE PROCEDURE maint_sales_summary_bytime();
Este é um resumo da documentação oficial em inglês e em português do Brasil, que devem ser consultados para informações mais detalhadas em: