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.
    

    おっとっと。一度セッションが別のセッションのコミットされていないデータを見ることができないので、これは決して機能しません。

    また、1つのステートメントに複数の行を挿入すると、テーブルの変更の問題が発生します。

    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が言ったように、制約はこのレベルではなく、データベースの奥深くに実装されています。

    複数のセッションがある場合は違います。また、1つのセッション内であっても、列にインデックスがない限り、パフォーマンスはcount(*)としてスケーリングされません。 徐々に遅くなります。そして、もしあなたがインデックスを持っているなら、そもそもそれをユニークなインデックスにしてみませんか?

    最後に、トリガー設計ガイドライン から :



    1. PostgreSQLを使用してすべてのデータベースを一覧表示する方法

    2. SQL Server Business Intelligence Development Studio で複数値パラメーターを一時テーブルに変換する

    3. MySQLでテーブルエイリアスを作成するにはどうすればよいですか

    4. PHPでアクティベーションリンクを期限切れにする方法は?