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

hvordan laver man en udløser som primær nøglebegrænsning?

Bare fordi du ser ud til at have til hensigt at se dette fejle og ikke tage noget fra APC's point, ser det ud til at virke ved første øjekast, så længe det er en 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;
/

Den kompilerer, og hvis du indsætter data, får du den adfærd, du ser ud til at ønske:

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 

Som ser ud til at gøre, hvad du vil. Men ikke hvis du har mere end én session. Jeg har ikke forpligtet mig i denne session; i en anden session kan jeg gøre:

insert into t42 values (1);

1 row created.

select * from t42;

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

1 row selected.

Hmm, det er mærkeligt. Nå, måske er det udskudt... lad os begå dem begge:

commit;

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

2 rows selected.

Ups. En gang session kan ikke se en anden sessions ikke-forpligtede data, så dette vil aldrig fungere.

Mutationstabelproblemet viser sig også, når vi indsætter flere rækker i en enkelt sætning:

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> 

Dobbelt ups.

Selv med et after trigger og en pakke til at løse problemet med muterende tabel, vil du stadig have dette problem (tror jeg), medmindre du låser hele tabellen for hver indsættelse eller opdatering. Som APC sagde, er begrænsningen implementeret dybt inde i databasen, ikke på dette niveau.

Ikke når du har mere end én session, nej. Og selv inden for én session, medmindre du har et indeks i kolonnen, skaleres ydeevnen ikke som count(*) bliver gradvist langsommere. Og hvis du har et indeks, ja, hvorfor så ikke gøre det til et unikt indeks i første omgang?

Endelig fra retningslinjerne for triggerdesign :



  1. Rette serialiserede data ødelagt på grund af redigering af MySQL-database i en teksteditor?

  2. Hvordan kan man få unikke værdier fra datatabel ved hjælp af dql?

  3. Meddelelse:count():Parameteren skal være en matrix eller et objekt, der implementerer Countable codeigniter på centos

  4. Hvordan bruger man DATEDIFF til at returnere år, måned og dag?