Oracle
 sql >> Base de données >  >> RDS >> Oracle

comment faire un déclencheur comme une contrainte de clé primaire?

Juste parce que vous semblez déterminé à voir cet échec et à ne rien enlever aux points d'APC, cela semble fonctionner à première vue tant qu'il s'agit d'un before déclencheur :

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

Il compile et si vous insérez des données, vous obtenez le comportement que vous semblez vouloir :

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 

Qui semble faire ce que tu veux. Mais pas si vous avez plus d'une session. Je ne me suis pas engagé dans cette session ; dans une autre session je peux faire :

insert into t42 values (1);

1 row created.

select * from t42;

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

1 row selected.

Hum, c'est étrange. Eh bien, peut-être que c'est différé... engageons-les tous les deux :

commit;

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

2 rows selected.

Oops. Une fois que la session ne peut pas voir les données non validées d'une autre session, cela ne fonctionnera donc jamais.

De plus, le problème de la table en mutation se manifeste lorsque nous insérons plusieurs lignes dans une seule instruction :

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> 

Double oups.

Même avec un after trigger et un package pour contourner le problème de la table de mutation, vous auriez toujours ce problème (je pense), à ​​moins que vous ne verrouilliez la table entière pour chaque insertion ou mise à jour. Comme l'APC l'a dit, la contrainte est implémentée profondément dans les entrailles de la base de données, pas à ce niveau.

Pas quand vous avez plus d'une session, non. Et même au sein d'une session, à moins que vous n'ayez un index sur la colonne, les performances ne seront pas mises à l'échelle en tant que count(*) deviendra progressivement plus lent. Et si vous avez un index, eh bien, pourquoi ne pas en faire un index unique en premier lieu ?

Enfin, à partir des directives de conception des déclencheurs :