Por que NÃO estou recebendo um erro de tabela de mutação no gatilho?

11

É (ou pelo menos era) conhecido que você não pode usar instruções DML em uma tabela de mutação dentro de um gatilho. Um trecho da documentação do Oracle :

Uma tabela de mutação é uma tabela que está sendo modificada por uma instrução UPDATE, DELETE ou INSERT ou uma tabela que pode ser atualizada pelos efeitos de uma restrição DELETE CASCADE.

A sessão que emitiu a instrução acionadora não pode consultar ou modificar uma tabela mutante. Essa restrição impede que um gatilho veja um conjunto inconsistente de dados.

No entanto, não consigo entender por que esse gatilho de demonstração não está falhando com um erro "tabela de mutação" quando executo um insert into empSQL Developer ou SQL * Plus:

CREATE OR REPLACE TRIGGER emp_bri   
  BEFORE INSERT ON emp 
    FOR EACH ROW
BEGIN

  SELECT max(id) + 1 INTO :NEW.id FROM emp;
  UPDATE emp SET salary = 5000;

END emp_bri;

A inserção é concluída com êxito com o próximo idvalor e atualiza todos os empregistros. Estou usando o Oracle Database 11g Enterprise Edition Release 11.2.0.1.0. Eu li sobre gatilhos compostos, mas a amostra não os utiliza.

Centurião
fonte
1
Não relacionado à sua pergunta, mas: NÃO use select max(id)para atribuir números exclusivos. Apenas não. É simplesmente incorreto e não será dimensionado também.
a_horse_with_no_name
Sim, eu sei disso :) O exemplo provavelmente não é muito bom nesse caso ... Os valores de incremento automático devem ser definitivamente implementados usando sequências e gatilhos.
Centurion
Isso com certeza é estranho. Btw: aqui está um exemplo SQLFiddle sqlfiddle.com/#!4/9e59f/2
a_horse_with_no_name
Obrigado por compartilhar informações, link legal. Não sabia que há site deste tipo de testes Oracle SQL :)
Centurion
a_horse_with_no_name: Outro exemplo: violino-teste-2 (SET salário = salário + 10)
ypercubeᵀᴹ

Respostas:

12

Há uma exceção. Quando você define um before insertacionador de nível de linha em uma tabela e emite uma única INSERTinstrução de linha , o table is mutatingerro não será gerado. Mas se você definir o mesmo tipo de acionador e emitir uma INSERTinstrução de várias linhas , o erro será gerado. Aqui está um exemplo:

SQL> create table TB_TR_TEST(
  2    col1 number,
  3    col2 number
  4  )
  5  ;

Table created

SQL> create or replace trigger TR_TB_TR_TEST
  2  before insert on TB_TR_TEST
  3  for each row
  4  begin
  5    SELECT max(col1) + 1 INTO :NEW.col1
  6      FROM TB_TR_TEST;
  7    UPDATE TB_TR_TEST SET col2 = 5000;
  8  end;
  9  /

Trigger created

Aqui está uma insertinstrução de linha única , que não gera erro de tabela mutante:

SQL> insert into TB_TR_TEST(col1, col2) values(1,2);

1 row inserted

SQL> insert into TB_TR_TEST(col1, col2) values(3,5);

1 row inserted

SQL> commit;

Commit complete

Aqui está uma instrução de inserção de várias linhas, que irá gerar um erro de tabela mutante:

SQL> insert into TB_TR_TEST(col1, col2)
  2    select 1, 2
  3      from dual;

insert into TB_TR_TEST(col1, col2)
  select 1, 2
    from dual

ORA-04091: table HR.TB_TR_TEST is mutating, trigger/function may not see it
ORA-06512: at "HR.TR_TB_TR_TEST", line 2
ORA-04088: error during execution of trigger 'HR.TR_TB_TR_TEST'
Nicholas Krasnov
fonte
Isso parece ser o culpado. Você tem uma referência no manual para esse comportamento?
a_horse_with_no_name
@a_horse_with_no_name se você tiver acesso ao support.oracle.com, pesquise ID 132569.1( ORA-4091 on BEFORE ROW TRIGGER with INSERT .. into SELECT statement).
Nicholas Krasnov
2
Obrigado. Interessante o suficiente, essa exceção parece estar documentada apenas nos manuais 8i (!): Docs.oracle.com/cd/F49540_01/DOC/server.815/a68003/… (a seção " " Tabelas de mutação e restrição ")" t encontrar essa afirmação nos manuais atuais.
a_horse_with_no_name