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

como fazer um gatilho como restrição de chave primária?


Só porque você parece ter a intenção de ver isso falhar e não tirar nada dos pontos da APC, isso parece funcionar à primeira vista, desde que seja um before acionar:
create table t42 (id number);

create trigger trig42
before insert or update on t42
for each row
declare
  c number;
begin
  if :new.id is null then
    raise_application_error(-20001, 'ID is null');    
  end if;
  select count(*) into c from t42 where id = :new.id;
  if c > 0 then
    raise_application_error(-20002, 'ID is not unique');
  end if;
end;
/

Ele compila e, se você inserir dados, obterá o comportamento que parece desejar:
insert into t42 values (1);

1 rows inserted.

insert into t42 values (1);

Error starting at line 20 in command:
insert into t42 values (1)
Error report:
SQL Error: ORA-20002: ID is not unique
ORA-06512: at "STACKOVERFLOW.TRIG42", line 9
ORA-04088: error during execution of trigger 'STACKOVERFLOW.TRIG42'

insert into t42 values (null);

Error starting at line 22 in command:
insert into t42 values (null)
Error report:
SQL Error: ORA-20001: ID is null
ORA-06512: at "STACKOVERFLOW.TRIG42", line 5
ORA-04088: error during execution of trigger 'STACKOVERFLOW.TRIG42'

select * from t42;

        ID
----------
         1 

Que parece fazer o que você quer. Mas não se você tiver mais de uma sessão. Eu não me comprometi nesta sessão; em outra sessão eu posso fazer:
insert into t42 values (1);

1 row created.

select * from t42;

        ID
----------
         1

1 row selected.

Hum, isso é estranho. Bem, talvez seja adiado... vamos cometer os dois:
commit;

select * from t42;
        ID
----------
         1
         1

2 rows selected.

Ops. Uma vez que a sessão não pode ver os dados não confirmados de outra sessão, isso nunca funcionará.

Além disso, o problema da tabela mutante se apresenta quando inserimos várias linhas em uma única instrução:
SQL> insert into t42 select level+1 from dual connect by level <= 5; 
insert into t42 select level+1 from dual connect by level <= 5
            *
ERROR at line 1:
ORA-04091: table STACKOVERFLOW.T42 is mutating, trigger/function may not see it
ORA-06512: at "STACKOVERFLOW.TRIG42", line 7
ORA-04088: error during execution of trigger 'STACKOVERFLOW.TRIG42'


SQL> 

Ops duplos.

Mesmo com um after trigger e um pacote para contornar o problema da tabela mutante, você ainda teria esse problema (eu acho), a menos que você bloqueie a tabela inteira para cada inserção ou atualização. Como a APC disse, a restrição é implementada nas entranhas do banco de dados, não neste nível.

Não quando você tem mais de uma sessão, não. E mesmo em uma sessão, a menos que você tenha um índice na coluna, o desempenho não será dimensionado como o count(*) ficará progressivamente mais lento. E se você tiver um índice, bem, por que não torná-lo um índice exclusivo em primeiro lugar?

Por fim, nas diretrizes de design de gatilho :