sql >> Base de Datos >  >> RDS >> Oracle

¿Cómo hacer un activador como restricción de clave principal?

Solo porque parece tener la intención de ver que esto falla, y no quitarle nada a los puntos de APC, esto parece funcionar a primera vista siempre que sea un before disparador:

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

Se compila y si inserta datos obtiene el comportamiento que parece desear:

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 

Que parece hacer lo que quieres. Pero no si tienes más de una sesión. No me he comprometido en esta sesión; en otra sesión puedo hacer:

insert into t42 values (1);

1 row created.

select * from t42;

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

1 row selected.

Mmm, eso es extraño. Bueno, tal vez sea diferido... comprometámoslos a ambos:

commit;

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

2 rows selected.

Ups. Una vez que la sesión no puede ver los datos no confirmados de otra sesión, esto nunca funcionará.

Además, el problema de la tabla mutante se presenta cuando insertamos varias filas en una sola instrucción:

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> 

Doble ups.

Incluso con un after trigger y un paquete para solucionar el problema de la tabla de mutación, aún tendría este problema (creo), a menos que bloquee toda la tabla para cada inserción o actualización. Como dijo APC, la restricción se implementa profundamente en las entrañas de la base de datos, no en este nivel.

No cuando tienes más de una sesión, no. E incluso dentro de una sesión, a menos que tenga un índice en la columna, el rendimiento no escalará como el count(*) será progresivamente más lento. Y si tiene un índice, bueno, ¿por qué no convertirlo en un índice único en primer lugar?

Finalmente, de las directrices de diseño de activadores :