sql >> Database >  >> RDS >> Oracle

hoe maak je een trigger zoals primaire sleutelbeperking?

Gewoon omdat je van plan bent om dit te zien mislukken, en niet om iets af te nemen van de punten van APC, lijkt dit op het eerste gezicht te werken zolang het een before is 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;
/

Het compileert en als je gegevens invoert, krijg je het gedrag dat je lijkt te willen:

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 

Die lijkt te doen wat je wilt. Maar niet als je meer dan één sessie hebt. Ik heb me niet gecommitteerd in deze sessie; in een andere sessie kan ik doen:

insert into t42 values (1);

1 row created.

select * from t42;

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

1 row selected.

Hm, dat is vreemd. Nou, misschien is het uitgesteld... laten we ze allebei vastleggen:

commit;

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

2 rows selected.

Oeps. Een sessie kan de niet-toegewezen gegevens van een andere sessie niet zien, dus dit zal nooit werken.

Het probleem met de muterende tabel doet zich ook voor wanneer we meerdere rijen in een enkele instructie invoegen:

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> 

Dubbele oeps.

Zelfs met een after trigger en een pakket om het probleem met de muterende tabel te omzeilen, zou je dit probleem nog steeds hebben (denk ik), tenzij je de hele tabel vergrendelt voor elke invoeging of update. Zoals APC zei, wordt de beperking diep in de ingewanden van de database geïmplementeerd, niet op dit niveau.

Niet als je meer dan één sessie hebt, nee. En zelfs binnen één sessie, tenzij je een index op de kolom hebt, wordt de prestatie niet geschaald als de count(*) zal geleidelijk langzamer worden. En als u wel een index heeft, waarom zou u er dan niet in de eerste plaats een unieke index van maken?

Tot slot, uit de richtlijnen voor triggerontwerp :



  1. Fatale fout:lees het gedeelte Beveiliging van de handleiding om erachter te komen hoe u mysqld als root kunt uitvoeren

  2. Database-ontwerp:registreren en verifiëren

  3. Hoe definieer ik een ARRAY-kolom in een Sequel Postgresql-migratie?

  4. SQLAlchemy en joins, we hebben geen externe sleutels