sql >> Databasteknik >  >> RDS >> Oracle

hur gör man en trigger som primärnyckelbegränsning?

Bara för att du verkar ha för avsikt att se detta misslyckas, och inte ta något ifrån APC:s poäng, verkar det här fungera vid första anblicken så länge det är 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 kompilerar och om du infogar data får du det beteende du verkar vilja ha:

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 verkar göra vad du vill. Men inte om du har mer än en session. Jag har inte engagerat mig i denna session; i en annan session kan jag göra:

insert into t42 values (1);

1 row created.

select * from t42;

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

1 row selected.

Hmm, det är konstigt. Tja, det kanske är uppskjutet... låt oss begå dem båda:

commit;

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

2 rows selected.

Hoppsan. En gång kan sessionen inte se en annan sessions oengagerade data, så detta kommer aldrig att fungera.

Problemet med muterande tabeller visar sig också när vi infogar flera rader i ett enda uttalande:

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> 

Dubbla oj.

Även med ett after trigger och ett paket för att komma runt problemet med muterande tabell, skulle du fortfarande ha det här problemet (tror jag), om du inte låser hela tabellen för varje infogning eller uppdatering. Som APC sa är begränsningen implementerad djupt in i databasen, inte på denna nivå.

Inte när du har mer än en session, nej. Och även inom en session, såvida du inte har ett index i kolumnen, skalas inte prestandan som count(*) kommer att bli långsammare successivt. Och om du har ett index, ja, varför inte göra det till ett unikt index i första hand?

Slutligen, från riktlinjer för utlösardesign :



  1. 4 sätt att få en lista över scheman i SQL Server Agent (T-SQL)

  2. SQLite Välj

  3. Läser en blob från MySQL med Java

  4. PDO::PARAM för datum?