33.3. Regras para INSERT, UPDATE e DELETE

As regras definidas para INSERT, UPDATE e DELETE são significativamente diferentes das regras de visão descritas na seção anterior. Em primeiro lugar, porque os comandos CREATE RULE destas regras permitem mais opções:

Em segundo lugar, não modificam a árvore de comando diretamente. Em vez disso, criam zero ou mais árvores de comando novas, e podem abandonar a árvore original.

33.3.1. Como as regras de atualização funcionam

Tenha em mente a sintaxe

CREATE RULE nome_da_regra AS ON evento
    TO objeto [WHERE qualificação_da_regra]
    DO [ALSO|INSTEAD] [ação | (ações) | NOTHING];

No que vem a seguir, regras de atualização significa regras definidas para INSERT, UPDATE ou DELETE.

As regras de atualização são aplicadas pelo sistema de regras quando a relação do resultado e o tipo de comando da árvore de comando são iguais ao objeto e evento especificados no comando CREATE RULE. Para as regras de atualização o sistema cria uma lista de árvores do comando. Inicialmente a lista de árvores de comando está vazia. Podem haver zero (palavra chave NOTHING), uma, ou várias ações. Para simplificar, será vista uma regra com uma ação. Esta regra pode ter uma qualificação, ou não, e pode ser INSTEAD ou ALSO (padrão).

O que é uma qualificação de regra? É uma restrição que informa quando as ações da regra devem ser realizadas e quando não devem. Esta qualificação somente pode fazer referência às pseudorelações NEW e/ou OLD, que representam basicamente a relação fornecida como objeto (mas com um significado especial).

Portanto tem-se quatro casos que produzem as seguintes árvores de comando para uma regra de uma ação.

Sem qualificação e ALSO

a árvore de comando da ação da regra com a qualificação da árvore de comando original adicionada

Sem qualificação mas INSTEAD

a árvore de comando da ação da regra com a qualificação da árvore de comando original adicionada

Qualificação fornecida e ALSO

a árvore de comando da ação da regra com a qualificação da regra e a qualificação da árvore de comando original adicionada

Qualificação fornecida e INSTEAD

a árvore de comando da ação da regra com a qualificação da regra e a qualificação da árvore de comando original; e a árvore de comando original com a qualificação da regra negada adicionada.

Por fim, se a regra for ALSO, a árvore de comando original não modificada é adicionada à lista. Uma vez que somente as regras INSTEAD qualificadas adicionam a árvore de comando original, acaba-se com uma ou duas árvores de comando de saída em uma regra com uma ação.

Para as regras ON INSERT, o comando original (se não for suprimido pelo INSTEAD) é executado antes de qualquer ação adicionada pelas regras. Isto permite as ações enxergarem as linhas inseridas. Porém, para as regras ON UPDATE e ON DELETE o comando original é executado após as ações adicionadas pelas regras. Isto garante que as ações podem enxergar as linhas a serem atualizadas ou excluídas; caso contrário, as ações não podem fazer nada porque não encontram linhas correspondendo às suas qualificações.

As árvores de comando geradas a partir das ações das regras são lançadas no sistema de reescrita novamente, e talvez sejam aplicadas mais regras resultando em um número maior, ou menor, de árvores de comando. Portanto, as árvores de comando nas ações das regras devem ter um tipo de comando diferente ou uma relação do resultado diferente, senão este processo recursivo se torna um laço. Atualmente existe um limite de recursão estabelecido em 100 interações. Se após 100 interações ainda existirem regras de atualização a serem aplicadas, o sistema de regras assume que está ocorrendo um laço sobre várias definições de regra e relata um erro.

As árvores de comando encontradas nas ações do catálogo do sistema pg_rewrite são somente modelos (templates). Uma vez que podem fazer referência às entradas NEW e OLD da tabela de abrangência, devem ser feitas algumas substituições antes que possa ser utilizadas. Para toda referência a NEW, é procurado na lista de destino do comando original uma entrada correspondente. Se for encontrada, a expressão desta entrada substitui a referência. Senão, NEW significa o mesmo que OLD (para o UPDATE), ou é substituído por um valor nulo (para o INSERT). Toda referência a OLD é substituída por uma referência a uma entrada na tabela de abrangência que é a relação do resultado.

Após o sistema terminar de aplicar as regras de atualização, aplica as regras de visão às árvores de comando produzidas. As visões não podem inserir novas ações de atualização, portanto não é necessário aplicar regras de atualização à saída da reescrita da regra.

33.3.1.1. Uma primeira regra passo-a-passo

Digamos que se deseja acompanhar as alterações na coluna cad_sap_num_par_disp da relação tbl_cadarço. Para essa finalidade é criada uma tabela de acompanhamento, e uma regra que escreve sob condição uma entrada de acompanhamento quando é executada uma atualização na tabela tbl_cadarço.

CREATE TABLE tbl_cadarço_log (
    cad_sap_nome          text,     -- nome do cadarço do sapato
    cad_sap_num_par_disp  integer,  -- novo valor disponível
    log_quem              text,     -- quem fez isto
    log_quando            timestamp -- quando
);

CREATE RULE reg_cadarço_upd AS ON UPDATE TO tbl_cadarço
    WHERE NEW.cad_sap_num_par_disp <> OLD.cad_sap_num_par_disp
    DO INSERT INTO tbl_cadarço_log VALUES (
                                    NEW.cad_sap_nome,
                                    NEW.cad_sap_num_par_disp,
                                    current_user,
                                    current_timestamp
                                );

Depois disso, se for executado

UPDATE tbl_cadarço SET cad_sap_num_par_disp = 6 WHERE cad_sap_nome = 'cad7';

e olhada a tabela de acompanhamento, será encontrado:

SELECT * FROM tbl_cadarço_log;

 cad_sap_nome | cad_sap_num_par_disp | log_quem |         log_quando
--------------+----------------------+----------+----------------------------
 cad7         |                    6 | teste    | 2005-12-03 07:45:28.500131
(1 linha)

Que é o esperado. O que aconteceu em segundo plano foi o seguinte: O analisador criou a árvore de comando

UPDATE tbl_cadarço SET cad_sap_num_par_disp = 6
  FROM tbl_cadarço tbl_cadarço
 WHERE tbl_cadarço.cad_sap_nome = 'cad7';

e existe a regra reg_cadarço_upd, que é do tipo ON UPDATE, contendo a expressão de qualificação de regra

NEW.cad_sap_num_par_disp <> OLD.cad_sap_num_par_disp

e a ação

INSERT INTO tbl_cadarço_log VALUES (
       *NEW*.cad_sap_nome, *NEW*.cad_sap_num_par_disp,
       current_user, current_timestamp )
  FROM tbl_cadarço *NEW*, tbl_cadarço *OLD*;

(Isto parece um pouco estranho, uma vez que normalmente não se pode escrever INSERT ... VALUES ... FROM. Neste caso, a cláusula FROM serve apenas para indicar que existem entradas na tabela de abrangência da árvore de comando para *NEW* *OLD*. São necessárias para que possam ser referenciadas pelas variáveis na árvore de comando do INSERT)

A regra é uma regra ALSO qualificada, portanto o sistema de regras precisa retornar duas árvores de comando: a ação da regra modificada e a árvore de comando original. No passo 1, a tabela de abrangência do comando original é incorporada à árvore de comando da ação da regra, resultando em:

INSERT INTO tbl_cadarço_log VALUES (
       *NEW*.cad_sap_nome, *NEW*.cad_sap_num_par_disp,
       current_user, current_timestamp )
  FROM tbl_cadarço *NEW*, tbl_cadarço *OLD*,
       tbl_cadarço tbl_cadarço;

No passo 2, a qualificação da regra é adicionada, ficando o conjunto de resultados restrito às linhas onde cad_sap_num_par_disp muda de valor:

INSERT INTO tbl_cadarço_log VALUES (
       *NEW*.cad_sap_nome, *NEW*.cad_sap_num_par_disp,
       current_user, current_timestamp )
  FROM tbl_cadarço *NEW*, tbl_cadarço *OLD*,
       tbl_cadarço tbl_cadarço
 WHERE *NEW*.cad_sap_num_par_disp <> *OLD*.cad_sap_num_par_disp;

(Isto parece ainda mais estranho, uma vez que INSERT ... VALUES também não tem uma cláusula a WHERE, mas o planejador e o executor não têm dificuldade para lidar com esta situação. De qualquer forma precisam dar suporte a esta mesma funcionalidade para INSERT ... SELECT.)

No passo 3 é adicionada a qualificação da árvore de comando original, restringindo o conjunto de resultados ainda mais, para somente as linhas afetadas pelo comando original:

INSERT INTO tbl_cadarço_log VALUES (
       *NEW*.cad_sap_nome, *NEW*.cad_sap_num_par_disp,
       current_user, current_timestamp )
  FROM tbl_cadarço *NEW*, tbl_cadarço *OLD*,
       tbl_cadarço tbl_cadarço
 WHERE *NEW*.cad_sap_num_par_disp <> *OLD*.cad_sap_num_par_disp
   AND tbl_cadarço.cad_sap_nome = 'cad7';

O passo 4 substitui as referências a NEW pelas entradas na lista de destino da árvore de comando original, ou pelas referências à variável correspondente da relação do resultado:

INSERT INTO tbl_cadarço_log VALUES (
       tbl_cadarço.cad_sap_nome, 6,
       current_user, current_timestamp )
  FROM tbl_cadarço *NEW*, tbl_cadarço *OLD*,
       tbl_cadarço tbl_cadarço
 WHERE 6 <> *OLD*.cad_sap_num_par_disp
   AND tbl_cadarço.cad_sap_nome = 'cad7';

O passo 5 troca as referências a OLD por referências a relação do resultado:

INSERT INTO tbl_cadarço_log VALUES (
       tbl_cadarço.cad_sap_nome, 6,
       current_user, current_timestamp )
  FROM tbl_cadarço *NEW*, tbl_cadarço *OLD*,
       tbl_cadarço tbl_cadarço
 WHERE 6 <> tbl_cadarço.cad_sap_num_par_disp
   AND tbl_cadarço.cad_sap_nome = 'cad7';

Está pronto. Uma vez que a regra é ALSO, a árvore de comando original também é enviada para a saída. Em resumo, a saída do sistema de regras é uma lista com duas árvores de comando que correspondem a estas instruções:

INSERT INTO tbl_cadarço_log VALUES (
       tbl_cadarço.cad_sap_nome, 6,
       current_user, current_timestamp )
  FROM tbl_cadarço
 WHERE 6 <> tbl_cadarço.cad_sap_num_par_disp
   AND tbl_cadarço.cad_sap_nome = 'cad7';

UPDATE tbl_cadarço SET cad_sap_num_par_disp = 6
 WHERE cad_sap_nome = 'cad7';

São executadas nesta ordem, e é exatamente isto o que a regra deveria fazer.

As substituições e as qualificações adicionadas garantem que se o comando original fosse, digamos,

UPDATE tbl_cadarço SET cad_sap_cor = 'verde'
 WHERE cad_sap_nome = 'cad7';

não seria escrito nenhuma entrada de acompanhamento. Neste caso, a árvore de comando original não contém a entrada na lista de destino para cad_sap_num_par_disp, portanto NEW.cad_sap_num_par_disp é substituído por tbl_cadarço.cad_sap_num_par_disp. Portanto, o comando extra gerado por esta regra é

INSERT INTO tbl_cadarço_log VALUES (
       tbl_cadarço.cad_sap_nome, tbl_cadarço.cad_sap_num_par_disp,
       current_user, current_timestamp )
  FROM tbl_cadarço
 WHERE tbl_cadarço.cad_sap_num_par_disp <> tbl_cadarço.cad_sap_num_par_disp
   AND tbl_cadarço.cad_sap_nome = 'cad7';

e a qualificação nunca será verdade.

A regra também funciona quando o comando original modifica várias linhas. Portanto, se for executado o comando

UPDATE tbl_cadarço SET cad_sap_num_par_disp = 0
 WHERE cad_sap_cor = 'preto';

de fato serão atualizadas quatro linhas (cad1, cad2, cad3 e cad4), mas cad3 já tem cad_sap_num_par_disp igual a zero. Neste caso, a qualificação original das árvores de comando é diferente, e isto resulta na geração da árvore de comando adicional

INSERT INTO tbl_cadarço_log
SELECT tbl_cadarço.cad_sap_nome, 0,
       current_user, current_timestamp
  FROM tbl_cadarço
 WHERE 0 <> tbl_cadarço.cad_sap_num_par_disp
   AND tbl_cadarço.cad_sap_cor = 'preto';

pela regra. Esta árvore de comando com certeza insere três novas entradas de acompanhamento. E isto está inteiramente correto.

Aqui pode ser visto porque é importante a árvore de comando original ser executada por último. Se o UPDATE tivesse sido executado primeiro, todos os valores das linhas já teriam sido definidos como zero e, portanto, o acompanhamento do INSERT não encontraria uma linha onde 0 <> tbl_cadarço.cad_sap_num_par_disp.

33.3.2. Cooperação com visões

Uma forma simples de proteger as relações das visões contra a possibilidade mencionada de alguém tentar executar os comandos INSERT, UPDATE ou DELETE nas mesmas, é deixando estas árvores de comando serem jogadas fora. Para isso são criadas as regras

CREATE RULE vis_sapato_ins_protege AS ON INSERT TO vis_sapato
    DO INSTEAD NOTHING;
CREATE RULE vis_sapato_upd_protege AS ON UPDATE TO vis_sapato
    DO INSTEAD NOTHING;
CREATE RULE vis_sapato_del_protege AS ON DELETE TO vis_sapato
    DO INSTEAD NOTHING;

Depois disso, se alguém tentar fazer uma destas operações na relação da visão vis_sapato, o sistema de regras aplica estas regras. Uma vez que as regras não possuem ação e são INSTEAD, a lista de árvores de comando resultante estará vazia, e todo o comando se transforma em nada, porque não há nada deixado para ser otimizado ou executado após o sistema de regras terminar de executar.

Uma utilização mais sofisticadas do sistema de regras, é para criar regras que reescrevem uma árvore de comando em outra que realiza a operação correta nas tabelas de verdade. Para se fazer isto na visão vis_cadarço, são criadas as seguintes regras:

CREATE RULE vis_cadarço_ins AS ON INSERT TO vis_cadarço
    DO INSTEAD
    INSERT INTO tbl_cadarço VALUES (
           NEW.cad_sap_nome,
           NEW.cad_sap_num_par_disp,
           NEW.cad_sap_cor,
           NEW.cad_sap_comp,
           NEW.cad_sap_unid
    );

CREATE RULE vis_cadarço_upd AS ON UPDATE TO vis_cadarço
    DO INSTEAD
    UPDATE tbl_cadarço
       SET cad_sap_nome = NEW.cad_sap_nome,
           cad_sap_num_par_disp = NEW.cad_sap_num_par_disp,
           cad_sap_cor = NEW.cad_sap_cor,
           cad_sap_comp = NEW.cad_sap_comp,
           cad_sap_unid = NEW.cad_sap_unid
     WHERE cad_sap_nome = OLD.cad_sap_nome;

CREATE RULE vis_cadarço_del AS ON DELETE TO vis_cadarço
    DO INSTEAD
    DELETE FROM tbl_cadarço
     WHERE cad_sap_nome = OLD.cad_sap_nome;

Agora é assumido que, de vez em quando, chega na loja um pacote de cadarços e uma longa lista de partes junto com este, mas que não se deseja atualizar manualmente a visão vis_cadarço toda vez que chega um destes pacotes. Para isso são criadas duas pequenas tabelas: uma é onde são inseridos os itens da lista de partes, e outra com um truque especial. Os comandos de criação destas tabelas são:

CREATE TABLE tbl_cadarço_chegada (
    cheg_nome    text,
    cheg_quant   integer
);

CREATE TABLE tbl_cadarço_ok (
    ok_nome     text,
    ok_quant    integer
);

CREATE RULE tbl_cadarço_ok_ins AS ON INSERT TO tbl_cadarço_ok
    DO INSTEAD
    UPDATE vis_cadarço
       SET cad_sap_num_par_disp = cad_sap_num_par_disp + NEW.ok_quant
     WHERE cad_sap_nome = NEW.ok_nome;

Agora a tabela tbl_cadarço_chegada pode ser preenchida com os dados da lista de partes:

INSERT INTO tbl_cadarço_chegada VALUES('cad3',10);
INSERT INTO tbl_cadarço_chegada VALUES('cad6',20);
INSERT INTO tbl_cadarço_chegada VALUES('cad8',20);

SELECT * FROM tbl_cadarço_chegada;

 cheg_nome | cheg_quant
-----------+------------
 sap3      |         10
 sap6      |         20
 sap8      |         20
(3 linhas)

Dando uma olhada rápida nos dados atuais encontramos:

SELECT * FROM vis_cadarço;

 cad_sap_nome | cad_sap_num_par_disp | cad_sap_cor | cad_sap_comp | cad_sap_unid | cad_sap_comp_cm
--------------+----------------------+-------------+--------------+--------------+-----------------
 cad1         |                    5 | preto       |           80 | cm           |              80
 cad2         |                    6 | preto       |          100 | cm           |             100
 cad7         |                    6 | marrom      |           60 | cm           |              60
 cad3         |                    0 | preto       |           35 | inch         |            88.9
 cad4         |                    8 | preto       |           40 | inch         |           101.6
 cad8         |                    1 | marrom      |           40 | inch         |           101.6
 cad5         |                    4 | marrom      |            1 | m            |             100
 cad6         |                    0 | marrom      |          0.9 | m            |              90
(8 linhas)

Agora os cadarços que chegaram são movidos com:

INSERT INTO tbl_cadarço_ok SELECT * FROM tbl_cadarço_chegada;

e verificado os resultados:

SELECT * FROM vis_cadarço ORDER BY cad_sap_nome;

 cad_sap_nome | cad_sap_num_par_disp | cad_sap_cor | cad_sap_comp | cad_sap_unid | cad_sap_comp_cm
--------------+----------------------+-------------+--------------+--------------+-----------------
 cad1         |                    5 | preto       |           80 | cm           |              80
 cad2         |                    6 | preto       |          100 | cm           |             100
 cad3         |                   10 | preto       |           35 | inch         |            88.9
 cad4         |                    8 | preto       |           40 | inch         |           101.6
 cad5         |                    4 | marrom      |            1 | m            |             100
 cad6         |                   20 | marrom      |          0.9 | m            |              90
 cad7         |                    6 | marrom      |           60 | cm           |              60
 cad8         |                   21 | marrom      |           40 | inch         |           101.6
(8 linhas)

SELECT * FROM tbl_cadarço_log;

 cad_sap_nome | cad_sap_num_par_disp | log_quem |         log_quando
--------------+----------------------+----------+----------------------------
 cad7         |                    6 | teste    | 2005-12-03 08:31:22.822485
 cad3         |                   10 | teste    | 2005-12-03 08:37:15.497084
 cad6         |                   20 | teste    | 2005-12-03 08:37:15.497084
 cad8         |                   21 | teste    | 2005-12-03 08:37:15.497084
(4 linhas)

É um longo caminho de INSERT ... SELECT até estes resultados, e a descrição da transformação da árvore de comando será a última neste capítulo. Primeiro existe a saída do analisador:

INSERT INTO tbl_cadarço_ok
SELECT tbl_cadarço_chegada.cheg_nome, tbl_cadarço_chegada.cheg_quant
  FROM tbl_cadarço_chegada tbl_cadarço_chegada, tbl_cadarço_ok tbl_cadarço_ok;

Depois é aplicada a primeira regra a tbl_cadarço_ok_ins resultando em

UPDATE vis_cadarço
   SET cad_sap_num_par_disp = vis_cadarço.cad_sap_num_par_disp + tbl_cadarço_chegada.cheg_quant
  FROM tbl_cadarço_chegada tbl_cadarço_chegada, tbl_cadarço_ok tbl_cadarço_ok,
       tbl_cadarço_ok *OLD*, tbl_cadarço_ok *NEW*,
       vis_cadarço vis_cadarço
 WHERE vis_cadarço.cad_sap_nome = tbl_cadarço_chegada.cheg_nome;

e jogado fora o INSERT original em tbl_cadarço_ok. Este comando reescrito é passado para o sistema de regras novamente, e a aplicação segunda regra a vis_cadarço_upd produz

UPDATE tbl_cadarço
   SET cad_sap_nome = vis_cadarço.cad_sap_nome,
       cad_sap_num_par_disp = vis_cadarço.cad_sap_num_par_disp + tbl_cadarço_chegada.cheg_quant,
       cad_sap_cor = vis_cadarço.cad_sap_cor,
       cad_sap_comp = vis_cadarço.cad_sap_comp,
       cad_sap_unid = vis_cadarço.cad_sap_unid
  FROM tbl_cadarço_chegada tbl_cadarço_chegada, tbl_cadarço_ok tbl_cadarço_ok,
       tbl_cadarço_ok *OLD*, tbl_cadarço_ok *NEW*,
       vis_cadarço vis_cadarço, vis_cadarço *OLD*,
       vis_cadarço *NEW*, tbl_cadarço tbl_cadarço
 WHERE vis_cadarço.cad_sap_nome = tbl_cadarço_chegada.cheg_nome
   AND tbl_cadarço.cad_sap_nome = vis_cadarço.cad_sap_nome;

Novamente esta regra é do tipo INSTEAD e a árvore de comando anterior é jogada fora. Deve ser observado que este comando ainda utiliza a visão vis_cadarço, mas o sistema de regras não termina neste passo, e portanto continua e aplica a regra _RETURN produzindo

UPDATE tbl_cadarço
   SET cad_sap_nome = s.cad_sap_nome,
       cad_sap_num_par_disp = s.cad_sap_num_par_disp + tbl_cadarço_chegada.cheg_quant,
       cad_sap_cor = s.cad_sap_cor,
       cad_sap_comp = s.cad_sap_comp,
       cad_sap_unid = s.cad_sap_unid
  FROM tbl_cadarço_chegada tbl_cadarço_chegada, tbl_cadarço_ok tbl_cadarço_ok,
       tbl_cadarço_ok *OLD*, tbl_cadarço_ok *NEW*,
       vis_cadarço vis_cadarço, vis_cadarço *OLD*,
       vis_cadarço *NEW*, tbl_cadarço tbl_cadarço,
       vis_cadarço *OLD*, vis_cadarço *NEW*,
       tbl_cadarço s, tbl_unidade u
 WHERE s.cad_sap_nome = tbl_cadarço_chegada.cheg_nome
   AND tbl_cadarço.cad_sap_nome = s.cad_sap_nome;

Por fim a regra reg_cadarço_upd é aplicada produzindo a árvore de comando adicional

INSERT INTO tbl_cadarço_log
SELECT s.cad_sap_nome,
       s.cad_sap_num_par_disp + tbl_cadarço_chegada.cheg_quant,
       current_user,
       current_timestamp
  FROM tbl_cadarço_chegada tbl_cadarço_chegada, tbl_cadarço_ok tbl_cadarço_ok,
       tbl_cadarço_ok *OLD*, tbl_cadarço_ok *NEW*,
       vis_cadarço vis_cadarço, vis_cadarço *OLD*,
       vis_cadarço *NEW*, tbl_cadarço tbl_cadarço,
       vis_cadarço *OLD*, vis_cadarço *NEW*,
       tbl_cadarço s, tbl_unidade u,
       tbl_cadarço *OLD*, tbl_cadarço *NEW*
       tbl_cadarço_log tbl_cadarço_log
 WHERE s.cad_sap_nome = tbl_cadarço_chegada.cheg_nome
   AND tbl_cadarço.cad_sap_nome = s.cad_sap_nome
   AND (s.cad_sap_num_par_disp + tbl_cadarço_chegada.cheg_quant) <> s.cad_sap_num_par_disp;

Após isto o sistema de regras esgota as regras, e retorna as árvores de comando geradas.

Desta maneira se termina com duas árvores de comando finais equivalentes às instruções SQL

INSERT INTO tbl_cadarço_log
SELECT s.cad_sap_nome,
       s.cad_sap_num_par_disp + tbl_cadarço_chegada.cheg_quant,
       current_user,
       current_timestamp
  FROM tbl_cadarço_chegada tbl_cadarço_chegada, tbl_cadarço tbl_cadarço,
       tbl_cadarço s
 WHERE s.cad_sap_nome = tbl_cadarço_chegada.cheg_nome
   AND tbl_cadarço.cad_sap_nome = s.cad_sap_nome
   AND s.cad_sap_num_par_disp + tbl_cadarço_chegada.cheg_quant <> s.cad_sap_num_par_disp;

UPDATE tbl_cadarço
   SET cad_sap_num_par_disp = tbl_cadarço.cad_sap_num_par_disp + tbl_cadarço_chegada.cheg_quant
  FROM tbl_cadarço_chegada tbl_cadarço_chegada,
       tbl_cadarço tbl_cadarço,
       tbl_cadarço s
 WHERE s.cad_sap_nome = tbl_cadarço_chegada.cad_sap_nome
   AND tbl_cadarço.cad_sap_nome = s.cad_sap_nome;

O resultado é que a inserção dos dados de uma relação em outra, mudados para atualizações em uma terceira, e mudados para atualizações em uma quarta mais o acompanhamento da atualização final em uma quinta, acaba reduzida a dois comandos.

Existe um pequeno detalhe um pouco feio. Olhando os dois comandos vê-se que a relação tbl_cadarço aparece duas vezes na tabela de abrangência, quando poderia com certeza ser reduzida para uma vez. O planejador não trata isto e, portanto, o plano de execução para a saída do sistema de regras do INSERT será

Nested Loop
  ->  Merge Join
        ->  Seq Scan
              ->  Sort
                    ->  Seq Scan on s
        ->  Seq Scan
              ->  Sort
                    ->  Seq Scan on tbl_cadarço_chegada
  ->  Seq Scan on tbl_cadarço

enquanto se fosse omitida a entrada adicional na tabela de abrangência seria

Merge Join
  ->  Seq Scan
        ->  Sort
              ->  Seq Scan on s
  ->  Seq Scan
        ->  Sort
              ->  Seq Scan on tbl_cadarço_chegada

que produz exatamente as mesmas entradas na tabela de acompanhamento. Portanto, o sistema de regras causa uma varredura adicional não necessária na tabela tbl_cadarço, e a mesma varredura redundante é feita uma vez mais no UPDATE, mas foi realmente um trabalho duro tornar isto tudo possível.

Agora é feita uma demonstração final do sistema de regras do PostgreSQL e de seu poder. Digamos que se deseja adicionar alguns cadarços com cores extraordinárias ao banco de dados:

INSERT INTO vis_cadarço VALUES ('cad9', 0, 'ciano', 35.0, 'inch', 0.0);
INSERT INTO vis_cadarço VALUES ('cad10', 1000, 'magenta', 40.0, 'inch', 0.0);

Deseja-se construir uma visão para verificar quais entradas em vis_cadarço não correspondem a nenhuma cor de sapato. A visão para esta finalidade é

CREATE VIEW vis_cadarço_não_combina AS
    SELECT * FROM vis_cadarço WHERE NOT EXISTS
        (SELECT sap_nome FROM vis_sapato WHERE sap_cor_cad_pref = cad_sap_cor);

e sua saída é

SELECT * FROM vis_cadarço_não_combina;

 cad_sap_nome | cad_sap_num_par_disp | cad_sap_cor | cad_sap_comp | cad_sap_unid | cad_sap_comp_cm
--------------+----------------------+-------------+--------------+--------------+-----------------
 cad10        |                 1000 | magenta     |           40 | inch         |           101.6
 cad9         |                    0 | ciano       |           35 | inch         |            88.9
(2 linhas)

Agora desejamos fazer com que os cadarços cuja cor não corresponde a nenhuma cor de sapato, e que não estão no estoque, sejam eliminados do banco de dados. Para tornar as coisas um pouco mais difícil para o PostgreSQL, a exclusão não é feita diretamente. Em vez disso, é criada mais uma visão

CREATE VIEW vis_cadarço_pode_excluir AS
    SELECT * FROM vis_cadarço_não_combina WHERE cad_sap_num_par_disp = 0;

e feito desta maneira:

DELETE FROM vis_cadarço WHERE EXISTS
    (SELECT * FROM vis_cadarço_pode_excluir
             WHERE cad_sap_nome = vis_cadarço.cad_sap_nome);

Voilà :

SELECT * FROM vis_cadarço;

 cad_sap_nome | cad_sap_num_par_disp | cad_sap_cor | cad_sap_comp | cad_sap_unid | cad_sap_comp_cm
--------------+----------------------+-------------+--------------+--------------+-----------------
 cad1         |                    5 | preto       |           80 | cm           |              80
 cad2         |                    6 | preto       |          100 | cm           |             100
 cad7         |                    6 | marrom      |           60 | cm           |              60
 cad4         |                    8 | preto       |           40 | inch         |           101.6
 cad3         |                   10 | preto       |           35 | inch         |            88.9
 cad8         |                   21 | marrom      |           40 | inch         |           101.6
 cad10        |                 1000 | magenta     |           40 | inch         |           101.6
 cad5         |                    4 | marrom      |            1 | m            |             100
 cad6         |                   20 | marrom      |          0.9 | m            |              90
(9 linhas)

Um comando DELETE em uma visão, com uma qualificação de de subcomando que no total utiliza 4 visões aninhadas/juntadas, onde uma delas possui uma qualificação de subcomando contendo uma visão e onde são utilizadas colunas da visão calculadas, acaba reescrita em uma única árvore de comando que exclui os dados requisitados da tabela real.

Provavelmente existem poucas situações no mundo real onde uma construção deste tipo é necessária, mas faz bem saber que funciona.

SourceForge.net Logo CSS válido!