とにかく、データをもう一方に複製するトリガーを2つのテーブルに追加できるかどうかを知りたいです。
例えば:
2つのusersテーブルusers_V1とusers_V2があります。ユーザーがV1アプリのいずれかで更新されると、users_V2でもそれを更新するトリガーがアクティブになります。
ユーザーがV2で更新されたときに、V1のデータを更新するためにV2テーブルに同じトリガーを追加したい場合、それは無限ループに入りますか?それを回避する方法はありますか?
とにかく、データをもう一方に複製するトリガーを2つのテーブルに追加できるかどうかを知りたいです。
例えば:
2つのusersテーブルusers_V1とusers_V2があります。ユーザーがV1アプリのいずれかで更新されると、users_V2でもそれを更新するトリガーがアクティブになります。
ユーザーがV2で更新されたときに、V1のデータを更新するためにV2テーブルに同じトリガーを追加したい場合、それは無限ループに入りますか?それを回避する方法はありますか?
処理中にトリガーを明示的に無効にすることはお勧めしません。これにより、奇妙な副作用が発生する可能性があります。
トリガーのサイクルを検出(および防止)する最も信頼できる方法は、を使用することCONTEXT_INFO()
です。
例:
CREATE TRIGGER tr_Table1_Update
ON Table1
FOR UPDATE AS
DECLARE @ctx VARBINARY(128)
SELECT @ctx = CONTEXT_INFO()
IF @ctx = 0xFF
RETURN
SET @ctx = 0xFF
-- Trigger logic goes here
より詳細な例については、このリンクを参照してください。
CONTEXT_INFO()
SQL Server2000での注意:
コンテキスト情報はサポートされていますが、CONTEXT_INFO
関数はサポートされていないようです。代わりにこれを使用する必要があります。
SELECT @ctx = context_info
FROM master.dbo.sysprocesses
WHERE spid = @@SPID
TRIGGER_NESTLEVEL()を使用してトリガーの再帰を制限するか、
UPDATEが必要かどうかをターゲットテーブルで確認します。
IF (SELECT COUNT(1)
FROM users_V1
INNER JOIN inserted ON users_V1.ID = inserted.ID
WHERE users_V1.field1 <> inserted.field1
OR users_V1.field2 <> inserted.field2) > 0 BEGIN
UPDATE users_V1 SET ...
私はまったく同じ問題を抱えていました。CONTEXT_INFO()を使用してみましたが、これはセッション変数であるため、初めて機能します。その後、セッション中にトリガーが起動したとき、これは機能しません。そのため、影響を受けた各トリガーでネストレベルを返す変数を使用して終了することになりました。
CREATE TRIGGER tr_Table1_Update
ON Table1
FOR UPDATE AS
BEGIN
--Prevents Second Nested Call
IF @@NESTLEVEL>1 RETURN
--Trigger logic goes here
END
注:ネストされたすべての呼び出しを停止する場合は、@@NESTLEVEL>0を使用します
もう1つの注意-この記事では、ネストされた呼び出しと再帰的な呼び出しについて多くの混乱があるようです。元のポスターは、あるトリガーが別のトリガーを起動し、最初のトリガーが再度起動する、ネストされたトリガーを参照していました。これはネストされていますが、SQL Serverによると、トリガーがそれ自体を直接呼び出し/トリガーしていないため、再帰的ではありません。再帰は、「1つのトリガーが別のトリガーを呼び出す」場所ではありません。これはネストされていますが、必ずしも再帰的ではありません。これをテストするには、再帰とネストを有効/無効にして、ここに記載されているいくつかの設定を使用します。ネストに関するブログ投稿
私は、この特定の設計シナリオのトリガーキャンプがありません。そうは言っても、私はあなたのアプリが何をするのか、そしてなぜそれをするのかについて私が持っている限られた知識で、これが私の全体的な分析です:
テーブルでトリガーを使用すると、テーブルのすべてのアクションに対応できるという利点があります。それだけです、この場合のあなたの主な利点。ただし、これは、テーブルに直接アクセスできるユーザー、またはテーブルに複数のアクセスポイントを持つユーザーがいることを意味します。私はそれを避ける傾向があります。トリガーには場所があります(私はそれらを頻繁に使用します)が、コンテキスト(一般的には強み)についてあまり知らない傾向があり、必要な場所で使用すると、これは私が使用する最後のデータベース設計ツールの1つです。さまざまなコンテキストと全体的なユースケースについて知るために、それらの利点は弱められています。
両方のアプリバージョンが同じアクションをトリガーする必要がある場合は、両方が同じストアドプロシージャを呼び出す必要があります。ストアドプロシージャは、すべての適切な作業が行われることを保証できます。アプリがV1をサポートする必要がなくなったら、ストアドプロシージャのその部分を削除できます。
クライアントコードで2つのストアドプロシージャを呼び出すことはお勧めできません。これは、アプリケーションが心配することなく、データベースが簡単かつ一貫して提供できるデータサービスの抽象化レイヤーだからです。
ビュー、UDF、またはSPのいずれかを使用して、基になるテーブルへのインターフェイスをより制御することを好みます。ユーザーがテーブルに直接アクセスすることはありません。ここでのもう1つのポイントは、ユーザーが知らないうちに、適切な基になるテーブルを結合する単一の「ユーザー」VIEWまたはUDFを提示できることです。おそらく、新しい属性が含まれているため、「同期」さえ必要ないポイントに到達します。そのような病理学的柔軟性が必要な場合、またはまだ結合できる他の異なる構造が必要な場合は、EAVシステム-たとえば、OUTERAPPLYUDFなど。
次のようなことを試してください(私はしませんでした;あなたはすでにその部分を書く方法を知っているので、createトリガーのものを気にしませんでした):
update t
set field1 = i.field1
field2 = i.field2
from inserted i
join table1 t on i.id = t.id
where field1 <> i.field1 OR field2 <> i.field2
トリガー内にある種のループバック検出を作成する必要があります。おそらく、次のテーブルに入力する前に、「ifexists」ステートメントを使用してレコードが存在するかどうかを確認します。現在設定されている方法で無限ループに入るように聞こえます。
トリガーの再帰、つまり、あるトリガーが別のトリガーを呼び出すのは、32レベルに制限されています
各トリガーで、挿入する行がすでに存在するかどうかを確認します。
例
CREATE TRIGGER Table1_Synchronize_Update ON [Table1] FOR UPDATE AS
BEGIN
UPDATE Table2
SET LastName = i.LastName
, FirstName = i.FirstName
, ... -- Every relevant field that needs to stay in sync
FROM Table2 t2
INNER JOIN Inserted i ON i.UserID = t2.UserID
WHERE i.LastName <> t2.LastName
OR i.FirstName <> t2.FirstName
OR ... -- Every relevant field that needs to stay in sync
END
CREATE TRIGGER Table1_Synchronize_Insert ON [Table1] FOR INSERT AS
BEGIN
INSERT INTO Table2
SELECT i.*
FROM Inserted i
LEFT OUTER JOIN Table2 t2 ON t2.UserID = i.UserID
WHERE t2.UserID IS NULL
END
CREATE TRIGGER Table2_Synchronize_Update ON [Table2] FOR UPDATE AS
BEGIN
UPDATE Table1
SET LastName = i.LastName
, FirstName = i.FirstName
, ... -- Every relevant field that needs to stay in sync
FROM Table1 t1
INNER JOIN Inserted i ON i.UserID = t1.UserID
WHERE i.LastName <> t1.LastName
OR i.FirstName <> t1.FirstName
OR ... -- Every relevant field that needs to stay in sync
END
CREATE TRIGGER Table2_Synchronize_Insert ON [Table2] FOR INSERT AS
BEGIN
INSERT INTO Table1
SELECT i.*
FROM Inserted i
LEFT OUTER JOIN Table1 t1 ON t1.UserID = i.UserID
WHERE t1.UserID IS NULL
END
疫病のようなトリガーを避けてください....ストアドプロシージャを使用してユーザーを追加します。これにいくつかの設計変更が必要な場合は、それらを変更します。トリガーはEVILです。