Mutating Tables em Oracle PL/SQL

Após este artigo, você será capaz de:

  • Entender Tabelas Mutantes
  • Resolver Problemas de Tabelas Mutantes

O que é uma Tabela Mutante?

Imagine que uma tabela no banco de dados é como uma grande planilha. Quando você faz uma alteração nessa planilha (como adicionar, atualizar ou excluir uma linha), é como se a planilha estivesse “em obras”. Durante esse momento de mudança, chamamos essa tabela de “mutante”.

Por que isso é um problema?

O problema surge quando um trigger (uma espécie de alarme automático do banco de dados) tenta ler ou modificar essa tabela enquanto ela está “em obras”. É como se você tentasse ler um livro enquanto alguém está arrancando ou colando páginas nele!

Um exemplo do cotidiano

Pense numa fila de supermercado. Você está na fila (que representa a tabela) e decide trocar de fila (uma operação de UPDATE). Agora, imagine que exista uma regra: se a nova fila tiver mais de 10 pessoas, você não pode entrar nela. Para verificar isso, você precisa contar quantas pessoas estão na fila. Mas e se, exatamente nesse momento, outras pessoas estiverem entrando ou saindo da fila? Sua contagem seria confusa e possivelmente incorreta. Essa é a essência do problema das tabelas mutantes!

O exemplo técnico explicado

Considere o seguinte exemplo de um trigger causando um erro de tabela mutante:

Este trigger dispara antes de uma instrução INSERT ou UPDATE ser emitida na tabela EMPLOYEES. O trigger verifica se o departamento especificado tem muitos funcionários. Se o número de funcionários em um departamento for igual ou maior que 45, o trigger emite uma mensagem de erro indicando que o departamento está com excesso de pessoal.

Agora, considere a seguinte instrução UPDATE emitida contra a tabela EMPLOYEES:

Quando esta instrução UPDATE é emitida contra a tabela EMPLOYEES, a seguinte mensagem de erro é exibida:

Observe que a mensagem de erro afirma que a tabela EMPLOYEES está mutante e o trigger não pode vê-la. Esta mensagem de erro é gerada porque há uma instrução SELECT INTO, emitida contra a tabela EMPLOYEES que está sendo modificada e, portanto, está mutante.

Por que o erro ocorre?

Quando você executa:

É como se você estivesse movendo um funcionário para outro departamento. O trigger tenta contar quantos funcionários já estão nesse departamento, mas a tabela está “em obras” (mutando) porque você está fazendo uma alteração nela.

A mensagem de erro decodificada

Quando o banco de dados diz:

Ele está basicamente gritando: “Ei, não posso contar os funcionários agora, a tabela está mudando!”

 

Resolvendo Problemas de Tabelas Mutantes

Para corrigir o erro de tabela mutante descrito anteriormente, os seguintes passos devem ser tomados ao usar uma versão do Oracle anterior à 11g:

  1. Para registrar o ID do departamento e o nome conforme descrito no exemplo anterior, duas variáveis globais devem ser declaradas com a ajuda de um pacote PL/SQL.
  2. O trigger existente deve ser modificado para que ele registre o ID do departamento, consulte a tabela DEPARTMENTS e registre o nome do departamento.
  3. Um novo trigger deve ser criado na tabela EMPLOYEES. Este trigger deve ser um trigger de nível de instrução que dispara após a instrução INSERT ou UPDATE ter sido emitida. Ele verificará o número de funcionários em um determinado departamento e gerará um erro se esse número for igual ou maior que 45.

Considere a especificação do pacote a seguir:

Esta especificação de pacote contém declarações para duas variáveis globais, g_department_id e g_department_name.

Em seguida, o trigger EMPLOYEES_BIU existente é modificado da seguinte forma:

Finalmente, um novo trigger de nível de instrução é criado na tabela EMPLOYEES:

Este trigger dispara após uma instrução INSERT ou UPDATE ser emitida contra a tabela EMPLOYEES. Ele verifica o número de funcionários em um determinado departamento e gera um erro se esse número for igual ou maior que 45.

Como resultado, a instrução UPDATE usada anteriormente produzirá o erro ORA-20000 conforme esperado:

Este erro é gerado pelo trigger EMPLOYEES_AIU e não contém nenhuma mensagem sobre uma tabela mutante.

Agora considere uma instrução UPDATE similar para um departamento diferente que não causa nenhum erro:

Esta abordagem resolve o problema da tabela mutante, permitindo que você implemente a lógica de negócios desejada sem violar as restrições do Oracle sobre tabelas mutantes.

Considerações Adicionais sobre Tabelas Mutantes

Compound Triggers (Oracle 11g e posterior)

A partir do Oracle 11g, uma solução mais elegante para o problema de tabelas mutantes é o uso de compound triggers. Um compound trigger permite definir diferentes seções de código para diferentes momentos de disparo em um único trigger.

Exemplo de um compound trigger que resolve o problema de tabela mutante:

Este compound trigger resolve o problema de tabela mutante de forma mais eficiente:

  1. Na seção BEFORE STATEMENT, inicializamos um array associativo com a contagem atual de funcionários por departamento.
  2. Na seção BEFORE EACH ROW, atualizamos as contagens conforme as linhas são modificadas.
  3. Na seção AFTER EACH ROW, verificamos se o departamento excedeu o limite de funcionários.

Contexto de Sistema

Outra abordagem para resolver problemas de tabelas mutantes é usar o contexto do sistema. Esta técnica pode ser útil quando você precisa compartilhar informações entre diferentes triggers ou entre triggers e aplicações.

Exemplo usando contexto de sistema:

Esta abordagem usa o contexto do sistema para armazenar e atualizar as contagens de funcionários por departamento, evitando assim o problema de tabela mutante.

Considerações de Desempenho

Ao lidar com tabelas mutantes, é importante considerar o impacto no desempenho:

  1. Compound Triggers: Geralmente oferecem melhor desempenho, pois permitem que você faça mais trabalho em uma única passagem pelos dados.
  2. Contexto de Sistema: Pode ter um pequeno overhead devido às chamadas de função para definir e obter valores do contexto.
  3. Pacotes com Variáveis Globais: Podem ser uma solução simples, mas podem não escalar bem em ambientes com muitos usuários concorrentes.

Melhores Práticas

  1. Minimize o Uso: Sempre que possível, projete seu esquema e lógica de negócios para evitar a necessidade de acessar tabelas mutantes em triggers.
  2. Use Compound Triggers: Se estiver usando Oracle 11g ou posterior, compound triggers geralmente são a melhor solução para problemas de tabelas mutantes.
  3. Considere Alternativas: Em alguns casos, pode ser melhor mover a lógica para procedimentos armazenados ou para a camada de aplicação.
  4. Teste Cuidadosamente: Certifique-se de testar suas soluções para tabelas mutantes com diferentes cenários, incluindo operações em lote e concorrência.
  5. Documente: Dado que as soluções para tabelas mutantes podem ser complexas, é crucial documentar bem a lógica e o raciocínio por trás da sua implementação.

Concluindo, lidar com tabelas mutantes é um desafio comum no desenvolvimento de triggers Oracle. As soluções apresentadas aqui – usando pacotes, compound triggers e contextos de sistema – oferecem diferentes abordagens para resolver este problema, cada uma com suas próprias vantagens e considerações. A escolha da melhor solução dependerá das necessidades específicas do seu aplicativo, da versão do Oracle que você está usando e dos requisitos de desempenho.

Scroll to Top