Oracle
 sql >> база данни >  >> RDS >> Oracle

как да направя тригер като ограничение на първичен ключ?

Само защото изглеждате възнамерени да видите това неуспешно и да не отнемате нищо от точките на APC, това изглежда работи на пръв поглед, стига да е before тригер:

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

Той се компилира и ако вмъкнете данни, получавате поведението, което изглежда искате:

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 

Което изглежда прави това, което искате. Но не и ако имате повече от една сесия. Не съм се ангажирал в тази сесия; в друга сесия мога да направя:

insert into t42 values (1);

1 row created.

select * from t42;

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

1 row selected.

Хм, това е странно. Е, може би е отложено... нека ги ангажираме и двамата:

commit;

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

2 rows selected.

опа Веднъж сесията не може да види необвързаните данни на друга сесия, така че това никога няма да работи.

Освен това проблемът с променящата се таблица се проявява, когато вмъкваме няколко реда в един израз:

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> 

Двойно опа.

Дори и с after тригер и пакет за заобикаляне на проблема с мутиращата таблица, все още ще имате този проблем (мисля), освен ако не заключите цялата таблица за всяко вмъкване или актуализиране. Както каза APC, ограничението е внедрено дълбоко в недрата на базата данни, а не на това ниво.

Не, когато имате повече от една сесия, не. И дори в рамките на една сесия, освен ако нямате индекс в колоната, ефективността няма да се мащабира като count(*) ще става прогресивно по-бавно. И ако имате индекс, добре, защо не го направите уникален индекс на първо място?

И накрая, от указания за проектиране на тригери :



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Не може да компилира PL/SQL с BULK COLLECT и FORALL

  2. Конфигурирайте mybatis да използва съществуваща връзка

  3. Възможно ли е да се изпълнят множество изрази в една заявка с помощта на DBD::Oracle?

  4. набор за актуализиране от вътрешна заявка за присъединяване, показваща грешка

  5. Функция ROUND(число) в Oracle