web-dev-qa-db-ja.com

挿入トリガーの代わりに意図的に再帰的にネストしたくない

自分自身を再帰的に呼び出すトリガーの代わりに取得するのに苦労しています。

テーブルに行が挿入されると、その時点で評価された特定の条件に基づいてトリガーが配置され、行データを変更して挿入するという考え方です。また、これらの条件が満たされた場合、2番目の行が同じテーブルに挿入され、トリガーが起動して、以前のように新しく挿入された値を評価し、データを変更して2番目の行を挿入します。 TRIGGER_NESTLEVEL()が特定の値(常に3未満)に達した。

私が思うに、2番目の挿入時にトリガーを再度実行するようには見えないようです。

これが私の問題を説明するために私が書いたサンプルコードです。

_--delete Tester1
--go
--drop table Tester1
alter database Playground
set recursive_triggers on

go

create table Tester1(ID int identity, Val int, String varchar(255))

go

Create trigger [tr_Tester1]
on Tester1
instead of insert
as
Begin

    if(Select TRIGGER_NESTLEVEL()) >= 3
    Return

    --Insert the initial row (modify text based on criterion)
    insert into Tester1
    select 
        inserted.Val,
        case
            when inserted.Val%2 = 0 
                then 'modified ' + inserted.String --Insert modified text
            else
                inserted.String --Insert the original text
        end
    from inserted

    --Insert a second row if the criterion was satisfied
    insert into Tester1
    select
        inserted.Val * 2, --Insert a new value that should satisfy the condition
        'some more text ' + CAST(TRIGGER_NESTLEVEL() as varchar(5))
    FROM inserted
    where
        inserted.Val%2 = 0

End

insert into Tester1(Val, String)
values(2,'some text')

select * from Tester1
_

私が見ている結果: enter image description here

期待していた結果: enter image description here

私はSELECT DATABASEPROPERTYEX('Playground', 'IsRecursiveTriggersEnabled')と_select * from sys.configurations where name like 'nested triggers'_を確認しましたが、 [〜#〜] msdn [〜#〜] に従って両方が有効になっています:

「これらのアクションは他のトリガーを開始することができます。DMLトリガーとDDLトリガーは最大32レベルまでネストできます。ネストされたトリガーのサーバー設定オプションを通じてAFTERトリガーをネストできるかどうかを制御できます。INSTEADOFトリガー(DMLトリガーのみがINSTEAD OFトリガー)は、この設定に関係なくネストできます。」

また、私の例では、私のロジックが常に再帰をもたらすことに気付いています(少なくとも、理論的には)。ただし、これは問題を説明するためだけのものです。実際のトリガーでは、これは必ずしもそうではありません。

誰かが私が欠けているものを指摘したり、私のアプローチが間違っている場合は教えてください。

2
ilikebeets

INSTEAD OFトリガーは、操作の実行時にそれ自体を直接呼び出すことはありません。

トリガーが行を挿入するのに、値が偶数の場合に限っては、最初のステートメントに非常に近く見え、再帰させたくないと考えてください。

CREATE TRIGGERページ から:

テーブルで定義されたINSTEAD OFトリガーが、通常は再度INSTEAD OFトリガーを起動するテーブルに対してステートメントを実行する場合、トリガーは再帰的に呼び出されません。代わりに、ステートメントはテーブルにINSTEAD OFトリガーがないかのように処理され、一連の制約操作とAFTERトリガー実行を開始します。

これは、INSTEAD OFを再帰の一部にすることができないという意味ではありません。トリガーが別のテーブルに変更を加える場合、それらはそれらのトリガーを呼び出します。トリガーは前後に跳ね返り、そのように再帰を作成できます。

元の挿入を制御するINSTEAD OFと追加の行を追加するAFTERトリガーを組み合わせることで、意図した動作を得ることができると思います。

2