Oracle
 sql >> Database >  >> RDS >> Oracle

come creare un trigger come il vincolo della chiave primaria?

Solo perché sembri intenzionato a vedere questo fallire e a non togliere nulla ai punti di APC, a prima vista sembra funzionare fintanto che è un before trigger:

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;
/

Si compila e se inserisci dati ottieni il comportamento che sembri desiderare:

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 

Che sembra fare quello che vuoi. Ma non se hai più di una sessione. Non ho commesso in questa sessione; in un'altra sessione posso fare:

insert into t42 values (1);

1 row created.

select * from t42;

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

1 row selected.

Hmm, è strano. Beh, forse è differito... impegniamoli entrambi:

commit;

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

2 rows selected.

Ops. Una volta che la sessione non può vedere i dati non vincolati di un'altra sessione, quindi non funzionerà mai.

Inoltre, il problema della tabella mutante si manifesta quando inseriamo più righe in una singola istruzione:

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> 

Doppio ops.

Anche con un after trigger e un pacchetto per aggirare il problema della tabella mutante, avresti ancora questo problema (credo), a meno che non blocchi l'intera tabella per ogni inserimento o aggiornamento. Come ha affermato APC, il vincolo è implementato nelle viscere del database, non a questo livello.

Non quando hai più di una sessione, no. E anche all'interno di una sessione, a meno che tu non abbia un indice nella colonna, il rendimento non verrà scalato come count(*) diventerà progressivamente più lento. E se hai un indice, beh, perché non renderlo un indice univoco in primo luogo?

Infine, dalle linee guida sulla progettazione dei trigger :