Oracle
 sql >> Datenbank >  >> RDS >> Oracle

Wie erstelle ich einen Trigger wie eine Primärschlüsselbeschränkung?

Nur weil Sie darauf bedacht zu sein scheinen, dass dies fehlschlägt, und um nichts von den Punkten von APC wegzunehmen, scheint dies auf den ersten Blick zu funktionieren, solange es ein before ist Auslöser:

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

Es wird kompiliert und wenn Sie Daten einfügen, erhalten Sie das gewünschte Verhalten:

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 

Was zu tun scheint, was Sie wollen. Aber nicht, wenn Sie mehr als eine Sitzung haben. Ich habe mich in dieser Sitzung nicht verpflichtet; in einer anderen Sitzung kann ich Folgendes tun:

insert into t42 values (1);

1 row created.

select * from t42;

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

1 row selected.

Hm, das ist seltsam. Nun, vielleicht ist es aufgeschoben... lasst uns beide festschreiben:

commit;

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

2 rows selected.

Hoppla. Sobald eine Sitzung die nicht festgeschriebenen Daten einer anderen Sitzung nicht sehen kann, wird dies niemals funktionieren.

Außerdem zeigt sich das Problem der mutierenden Tabelle, wenn wir mehrere Zeilen in eine einzelne Anweisung einfügen:

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> 

Doppelte Hoppla.

Sogar mit einem after Trigger und ein Paket, um das Problem der mutierenden Tabelle zu umgehen, haben Sie immer noch dieses Problem (glaube ich), es sei denn, Sie sperren die gesamte Tabelle für jede Einfügung oder Aktualisierung. Wie APC sagte, wird die Beschränkung tief in den Eingeweiden der Datenbank implementiert, nicht auf dieser Ebene.

Nicht, wenn Sie mehr als eine Sitzung haben, nein. Und selbst innerhalb einer Sitzung wird die Leistung nicht als count(*) skaliert, es sei denn, Sie haben einen Index für die Spalte wird immer langsamer. Und wenn Sie einen Index haben, warum machen Sie ihn nicht gleich zu einem eindeutigen Index?

Schließlich aus den Trigger-Designrichtlinien :