Oracle
 sql >> Base de Dados >  >> RDS >> Oracle

Usando IF EXISTS (SELECT ...) em um trigger BEFORE INSERT (Oracle)


Primeiro, se você estiver usando o SQL*Plus, quando criar um objeto e for informado de que há erros de compilação, o comando show errors mostrará os erros.

Se você executou show errors , você será informado de que IF EXISTS não é uma sintaxe válida. Você poderia fazer algo como
SELECT COUNT(*)
  INTO l_cnt
  FROM <<rest of query>>

IF( l_cnt > 0 )
THEN
  RAISE_APPLICATION_ERROR ...
END IF;

Depois de corrigir o erro de compilação, no entanto, você acabará com erros de tempo de execução. Em um gatilho em nível de linha em surveillance , geralmente você não pode consultar surveillance (você pode se tudo que você está fazendo é um INSERT VALUES que é garantido para inserir apenas uma única linha). Se você fizer isso, receberá um erro de gatilho mutante em tempo de execução.

Do ponto de vista do modelo de dados, quando você está projetando uma tabela na qual os dados válidos para uma linha específica dependem de dados armazenados em outras linhas da mesma tabela, você geralmente violou os princípios de normalização e geralmente está mais bem servido corrigindo o modelo de dados subjacente.

Se você estiver realmente determinado a manter o modelo de dados, prefiro criar uma visualização materializada que seja atualizada na confirmação e que tenha dados apenas para linhas que violam seus critérios. Você pode então colocar restrições nessa visão materializada que lançam erros no momento da confirmação quando seus critérios são violados. Isso exigirá logs de visualização materializados em sua tabela.

Se você realmente deseja manter o modelo de dados e deseja impor a lógica com gatilhos, precisa da solução clássica de três gatilhos (ou um gatilho composto com três partes se estiver usando a versão 11.2 ou posterior). Você criaria um pacote com uma coleção de valores de chave primária. Um gatilho de instrução antes inicializaria a coleção. Um gatilho em nível de linha inseriria as chaves primárias das linhas que foram inseridas e/ou atualizadas nesta coleção. E então um gatilho de instrução after iteraria sobre essa coleção e implementaria quaisquer verificações que você desejasse. No entanto, são muitas peças em movimento, e é por isso que geralmente aconselho contra isso.

Além disso, mesmo que todas essas peças funcionem, sua lógica não o protegerá em um ambiente multiusuário. Quando você tem vários usuários acessando o sistema ao mesmo tempo, é completamente possível que um usuário insira uma linha, o segundo usuário insira outra linha com um intervalo sobreposto e, em seguida, cada sessão será confirmada. Nesse caso, ambos os conjuntos de acionadores permitirão a alteração, mas você ainda terá dados na tabela que violam seus requisitos. A visão materializada, uma vez que é aplicada no momento do commit e não no momento da inserção, funcionará corretamente em um ambiente multiusuário. Se você quiser que os gatilhos funcionem em um ambiente multiusuário, você terá que complicá-los ainda mais adicionando lógica adicional que impõe a serialização que bloquearia o insert da segunda sessão da execução até a primeira sessão confirmada ou revertida. Isso adiciona complexidade, reduz a escalabilidade e, dependendo de como é implementado, pode ser um pesadelo de suporte.