39.7. Regras versus gatilhos #

Muitas coisas que podem ser feitas usando gatilhos, também podem ser implementadas usando o sistema de regras do PostgreSQL. Uma das coisas que não podem ser implementadas por regras são alguns tipos de restrições, especialmente chaves estrangeiras. É possível colocar uma regra qualificada que reescreve um comando para NOTHING, se o valor de uma coluna não aparecer em outra tabela. Mas então os dados são silenciosamente jogados fora, e isto não é uma boa ideia. Se forem necessárias verificações de valores válidos e, no caso de surgir um valor inválido, for gerada uma mensagem de erro, isto deverá ser feito por um gatilho.

Neste capítulo, o foco está no uso de regras para atualizar visões. Todos os exemplos de regra de atualização neste capítulo também podem ser implementados usando gatilhos INSTEAD OF nas visões. Escrever estes gatilhos é geralmente mais fácil do que escrever regras, especialmente se for necessária uma lógica complexa para executar a atualização.

Para as coisas que podem ser implementadas por ambos, qual é o melhor depende do uso do banco de dados. O gatilho é disparado uma vez para cada linha afetada. A regra modifica a consulta, ou gera uma consulta adicional. Portanto, se forem afetadas muitas linhas em uma instrução, uma regra que gera um comando extra provavelmente será mais rápida do que um gatilho chamado para cada linha devendo determinar novamente o que fazer várias vezes. No entanto, a abordagem de gatilho é conceitualmente muito mais simples do que a abordagem de regra, sendo mais fácil para os novatos acertarem.

A seguir está mostrado um exemplo de como a escolha de regras versus gatilhos funciona em uma situação. Existem duas tabelas:

CREATE TABLE computador (
    hospedeiro      text,    -- indexado
    fabricante      text     -- indexado
);

CREATE TABLE software (
    software        text,    -- indexado
    hospedeiro      text     -- indexado
);

As duas as tabelas possuem muitos milhares de linhas, e os índices no hospedeiro são únicos. A regra ou gatilho deve implementar uma restrição que exclua linhas de software que fazem referência a um computador excluído. O gatilho usaria o seguinte comando:

DELETE FROM software WHERE hospedeiro = $1;

Uma vez que o gatilho é chamado para cada linha individual excluída da tabela computador, pode-se preparar e salvar o plano para este comando, e passar o valor de hospedeiro no parâmetro. A regra seria escrita como:

CREATE RULE computador_exclusão AS ON DELETE TO computador
    DO DELETE FROM software WHERE hospedeiro = OLD.hospedeiro;

Agora, veremos diferentes tipos de exclusões. No caso de

DELETE FROM computador WHERE hospedeiro = 'mypc.local.net';

a tabela computador é verificada pelo índice (rápido), e o comando emitido pelo gatilho também usaria uma verificação de índice (também rápida). O comando extra da regra seria:

DELETE FROM software WHERE computador.hospedeiro = 'mypc.local.net'
                       AND software.hospedeiro = computador.hospedeiro;

Uma vez que existem índices apropriados definidos, o planejador irá criar o plano

Nestloop
  ->  Index Scan using comp_hostidx on computador
  ->  Index Scan using soft_hostidx on software

Portanto, não haveria muita diferença de velocidade entre o gatilho e a implementação da regra.

Na próxima exclusão desejamos nos livrar de todos os 2000 computadores onde hospedeiro começa com old. Existem dois comandos possíveis para fazer isto. Um é:

DELETE FROM computador WHERE hospedeiro >= 'old'
                       AND hospedeiro <  'ole'

O comando adicionado pela regra será

DELETE FROM software WHERE computador.hospedeiro >= 'old' AND computador.hospedeiro < 'ole'
                       AND software.hospedeiro = computador.hospedeiro;

com o plano

Hash Join
  ->  Seq Scan on software
  ->  Hash
    ->  Index Scan using comp_hostidx on computador

O outro comando possível é

DELETE FROM computador WHERE hospedeiro ~ '^old';

que resulta no seguinte plano de execução para o comando adicionado pela regra:

Nestloop
  ->  Index Scan using comp_hostidx on computador
  ->  Index Scan using soft_hostidx on software

Isto mostra que o planejador não percebe que a qualificação para hospedeiro em computador também pode ser usada para uma varredura de índice em software quando há várias expressões de qualificação combinadas com AND, que é o que é feito na versão do comando com expressão regular. O gatilho será chamado uma vez para cada um dos 2.000 computadores antigos que precisam ser excluídos, resultando em uma varredura de índice em computador e 2.000 varreduras de índice em software. A implementação da regra fará isto com dois comandos que usam índices. E depende do tamanho geral da tabela software se a regra ainda será mais rápida na situação de varredura sequencial. As 2.000 execuções de comandos do gatilho no gerenciador SPI levam algum tempo, mesmo que todos os blocos de índice logo estejam no cache.

O último comando que examinaremos é:

DELETE FROM computador WHERE fabricante = 'bim';

Novamente, isto pode resultar na exclusão de muitas linhas da tabela computador. Portanto, o gatilho executará novamente muitos comandos por meio do executor. O comando gerado pela regra será:

DELETE FROM software WHERE computador.fabricante = 'bim'
                       AND software.hospedeiro = computador.hospedeiro;

O plano para este comando será novamente o laço aninhado em duas varreduras de índice, usando apenas um índice diferente em computador:

Nestloop
  ->  Index Scan using comp_manufidx on computador
  ->  Index Scan using soft_hostidx on software

Em qualquer um desses casos, os comandos extras do sistema de regras serão mais ou menos independentes do número de linhas afetadas no comando.

O resumo é que as regras só serão muito mais lentas do que os gatilhos se suas ações resultarem em junções grandes e mal qualificadas, uma situação em que o planejador falha.