2016-03-22 13 views
0

自己参照テーブルのサイクルを制御するトリガーを作成しようとしています。 残念ながら私は誤りがあります。誰かが私が間違っていることを教えてもらえますか?サブクエリを使用したトリガー

CREATE OR REPLACE TRIGGER CHECK_CYCLE 
BEFORE INSERT OR UPDATE ON DEPARTMENTS 
FOR EACH ROW 
BEGIN 
IF :NEW.PARENT_ID = 
(SELECT ID, PARENT_ID, NAME, LEVEL, 
CONNECT_BY_ISLEAF AS ISLEAF, 
PRIOR NAME AS PARENT_NAME, 
CONNECT_BY_ROOT NAME AS ROOT 
FROM DEPARTMENTS 
START WITH PARENT_ID IS NULL 
CONNECT BY PRIOR ID = PARENT_ID) 
THEN 
RAISE_APPLICATION_ERROR(20000, 'Sorry.'); 
END IF; 
END; 

Error(9,25): PLS-00103: Encountered the symbol "AS" when expecting one of the following: 
     , from 
+0

クエリからすべての "AS"を削除しようとしましたか? – Hawk

+0

もちろん。助けにならない。 –

+0

あなたはどこでも(私が見る限り)使用されていないので、この部分があなたの質問の中で必要とされているのではないかと自分自身に問うべきです。 また、 'new'キーワードの隣にコロンを忘れる必要はありません。 –

答えて

0

これは、パーサが混乱しているように見えるようなものにそれを変更してみてください。 AS ROOTを完全に削除すると、予期したPLS-00405: subquery not allowed in this contextエラーに戻ります。なぜそれが混乱しているのか分かりません。あなたはそのクエリをスタンドアロンで実行することができます。しかし、あなたがしようとしていることはできないので、あまり心配する価値はないでしょう。

サブクエリにも3つの列があり、複数の行が返されるため、現在の行のスカラーPARENT_IDと比較すると機能しません。サブクエリーを別々に実行して、実際に関心のある値をローカル変数に選択してチェックすることができます。

しかし、あなたは挿入前のトリガーを持っていて、トリガーが反対のテーブルを照会しているので、挿入しようとすると突然のテーブル例外が発生します(または、一度に行)。

私はあなたが達成しようとしているものを理解していれば、あなたの代わりに後のINSERTトリガーを使用することができます。

create or replace trigger check_cycle 
after insert or update on departments 
declare 
    l_hascycle pls_integer; 
begin 
    select max(connect_by_iscycle) 
    into l_hascycle 
    from departments 
    start with parent_id is null 
    connect by nocycle prior id = parent_id; 

    if l_hascycle = 1 then 
    raise_application_error(-20000, 'Sorry.'); 
    end if; 
end; 
/

これは、何のサイクリングがない場合はすべての行に対してゼロになりますCONNECT_BY_ISCYCLE pseudocolumnを、使用していますサイクルする任意の列に対して1つです。そのmax()を選択してチェックすると、ローカルのl_hascycle変数に1つの値0または1が設定され、その変数を使用して例外をスローするかどうかを決定できます。最初の新しい行がOKであり、第二のサイクルを引き起こす

 ID PARENT_ID NAME  
---------- ---------- ------------ 
     1   Test   
     2   1 Test   

...:

insert into departments (id, parent_id, name) values (3, 2, 'OK'); 

1 row inserted. 

insert into departments (id, parent_id, name) values (2, 3, 'Cycles'); 

ORA-20000: Sorry. 
ORA-06512: at "SCHEMA.CHECK_CYCLE", line 11 
ORA-04088: error during execution of trigger 'SCHEMA.CHECK_CYCLE' 
のような既存の非サイクリングデータの上にテスト行のカップルを挿入

最初の挿入は(まだコミットされていない)効果にまだある、第二は、暗黙的にロールバックされました:

select * from departments: 

     ID PARENT_ID NAME  
---------- ---------- ------------ 
     1   Test   
     2   1 Test   
     3   2 OK   

これは、start with句のため、nullの親に達する既存のツリーに接続されていないデータのサイクルをキャッチしません。データを持つexampelの場合は、例外が発生することなく4,5,5,4を挿入することができます.4または5からヌルの親を持つ行へのルートがないためです。しかし、これは別の問題であり、別々にテストしたいことがあります。

他のセッションでコミットされていない変更のデータも表示されないため、2つのセッションが同時に独立して有効な行を挿入する可能性があります。

+0

あなたの決定に感謝します。 –

+0

しかし、私が試してみるとあなたのトリガは動作しません。私はこれを理解しているように、後挿入トリガはテーブルの既存のサイクルを検索します。私はあなたのサンプルデータを使用しました:部署の値(5,2、 'OK')に挿入してください。部門値(2、5、 'Cycles')に挿入します。この場合、変更は正常にコミットされました。 –

+0

挿入後のトリガーには、挿入したコミットされていないデータが表示されます。 (ただし、他のセッションからコミットされていない変更を見ることはできませんので、絶対確実ではありません)。トリガーを作成して、同じセッションから両方の挿入を行っている場合は、なぜそれが発射されず、例外がスローされないのか分かりません。 5または2のいずれかの階層全体が、親がヌルであるIDまでバックアップされていない場合を除き - そのペアが分離されている場合、 'start with'節はこれらの行を全く見ないクエリを停止します。 –

-1

私はそれがこのラインでだ、私はあなたのクエリでエラーを見つけたと思う - 正しく構築されていません

CONNECT_BY_ROOT NAME AS ROOT 

CONNECT_BY_ROOT AS ROOT 
+0

回答ありがとうございますが、CONNECT_BY_ROOT NAME AS ROOTは正しいです。エラー(5,3):PL/SQL:文が無視される エラー(6,3):PLS-00405:このコンテキストでサブクエリが許可されていない –

+0

本当にどのように正しいかわかりませんあなたは私の修正を使って試しましたか? –

+0

はい、私は試しました。サブクエリはトリガの外側で使用すると正しく動作します。 –

関連する問題