web-dev-qa-db-ja.com

SQL Server:現在のセッションでのみ更新のトリガーを無効にする方法

SQL Server 2008 R2に取り組んでいます。

tiu_benefitという名前のAFTER INSERT、UPDATEトリガーがあるテーブルbenefitがあります。

このテーブルのUPDATEステートメントを記述して1行を更新したいのですが、そのトリガーを起動したくありません。更新前にトリガーを無効にしてから、更新後にトリガーを有効にできることを知っています。

DISABLE TRIGGER tiu_benefit ON benefit;  
GO  
UPDATE benefit SET editor = 'srh' where benefit_id = 9876
GO
ENABLE TRIGGER tiu_benefit ON benefit;  
GO  

ただし、この無効化と有効化のトリガーは、現在ログインしているすべてのユーザーに影響します。したがって、スクリプトがトリガーを無効にしている間に別のユーザーがUPDATE/INSERTを実行する可能性があります。そのため、現在のセッションでトリガーの無効化と有効化のみを行います。出来ますか?はいの場合、方法を教えてください。

ありがとう

15
srh

私はこれについていくつかのテストを行いましたが、単一のトランザクションでプロセスを実行する場合は問題ないと思います。

BEGIN TRANSACTION
GO

DISABLE TRIGGER tiu_benefit ON benefit;
GO

UPDATE benefit
SET editor = 'srh'
WHERE benefit_id = 9876
GO

ENABLE TRIGGER tiu_benefit ON benefit;
GO

--Decide to commit or rollback

--commit
--rollback 

テストでは、最初にBEGIN TRANSACTIONDISABLE TRIGGERのみを強調表示して実行しました。次に、新しい(2番目の)クエリウィンドウを開いて、ベーステーブルに対してさまざまなDMLステートメント(SELECTINSERTUPDATEDELETE)を実行しようとしました。 2番目のクエリウィンドウでベーステーブルにアクセスするすべての試行は、明示的なトランザクションを使用してウィンドウが保持するロックを待機しました。明示的なトランザクションをコミット(またはロールバック)すると、2番目のウィンドウがテーブルにアクセスできるようになりました。

6
Scott Hodgin

あなたの問題を解決するために、私たちは問題に対してプログラム的なアプローチを取る必要があります。ここには2つのルートがあります。これらのアプローチが必要な理由は、特定のステートメントのトリガーを無効にすることはできず、テーブル全体に対してのみ無効にすることができるためです。

オプション1:Context_Info()

MS SQLのヒントのサミュエルヴァンガ の良い例がありました:

USE AdventureWorks; 
GO 
-- creating the table in AdventureWorks database 
IF OBJECT_ID('dbo.Table1') IS NOT NULL 
DROP TABLE dbo.Table1 
GO 
CREATE TABLE dbo.Table1(ID INT) 
GO 
-- Creating a trigger 
CREATE TRIGGER TR_Test ON dbo.Table1 FOR INSERT,UPDATE,DELETE 
AS 
DECLARE @Cinfo VARBINARY(128) 
SELECT @Cinfo = Context_Info() 
IF @Cinfo = 0x55555 
RETURN 
PRINT 'Trigger Executed' 
-- Actual code goes here 
-- For simplicity, I did not include any code 
GO

これで、サミュエルはトリガーを実行したくない場合、これを使用します。

SET Context_Info 0x55555 
INSERT dbo.Table1 VALUES(100)

Context_Infoは、次のシステムビューを使用して、現在のセッションに関する情報を取得します。

  • sys.dm_exec_requests

  • sys.dm_exec_sessions

  • sys.sysprocesses

ここでの考え方は、設定しているバイナリ文字列が現在のセッションにのみ公開されるため、トリガーがセッション中に実行されると、Context_info関数のスコープと変数設定が表示され、代わりにトリガーの一部をエスケープします。

オプション2:一時テーブル

Itzik Ben-Gan は、彼の著書 "Inside Microsoft SQL Server 2008 T-SQL Programming:T-SQL Programming"に 偉大な解決策 が含まれています。 T-SQLクエリcontext_info関数に対するこれの主な問題は、TempDBの小さなオーバーヘッドです。

驚きを台無しにして、本の陰謀を台無しにしないようにするには(購入して読む価値があると感じました)、トリガーを変更します。

トリガーは一時テーブルのチェックを実行する必要があります。一時テーブルが存在する場合、トリガーは終了してアクションを実行しないことを認識している必要があります。

実行するupdateステートメントで、最初に一時テーブルを作成します。トリガーと同じトランザクションで表示され、トリガーがステートメントを無視する原因になります。

トリガーの例:

CREATE TRIGGER TRIGGERNAME ON TABLENAME for INSERT AS

IF OBJECT_ID('tempdb..#FAKETEMPTABLE') IS NOT NULL RETURN;
GO

トリガーを実行させたくないときにステートメントを開始する例:

CREATE TABLE #FAKETEMPTABLE(col1 SMALLINT);

あなたの例のためにそれをまとめると:

ALTER TRIGGER tiu_benefit ON benefit FOR 
... 
AS
...
IF OBJECT_ID('tempdb..#FAKETEMPTABLE') IS NOT NULL RETURN;
--... rest of code here
GO

CREATE TABLE #FAKETEMPTABLE(col1 SMALLINT);
UPDATE benefit SET editor = 'srh' where benefit_id = 9876;
GO
18
Shaulinator

_CONTEXT_INFO_または新しい_SESSION_CONTEXT_を使用します。どちらもセッションベースの値です。

  • _CONTEXT_INFO_は、単一のVARBINARY(128)値です。これは、少なくともSQL Server 2000以降で使用できます。_CONTEXT_INFO_は、_VIEW SERVER STATE_ DMVによって返されるフィールドであるため、_sys.dm_exec_sessions_を持つすべてのユーザーが表示できます。私は以前にこれを使用したことがあり、それは非常にうまく機能します。

    SET CONTEXT_INFO で設定
    経由で取得 CONTEXT_INFO() または sys.dm_exec_sessions

    _CONTEXT_INFO_に格納する値のタイプによっては、注意が必要ないくつかのニュアンスがあります。それについては、次のブログ記事で説明します。

    CONTEXT_INFO()がSET CONTEXT_INFOによって設定された正確な値を返さないのはなぜですか?

  • Session_contextは、_SQL_VARIANT_値のキーと値のペアです。これはSQL Server 2016で導入されました。さまざまな目的での値の分離は非常に便利です。 Session_contextは、現在のセッションでのみ表示できます。

    sp_set_session_context でこの値を設定します
    SESSION_CONTEXT でこの値を取得します

ローカル一時テーブルオプション、さらにはトリガーの無効化/有効化オプションについて考慮すべき1つのこと:どちらも、ある程度のロックとトランザクションログアクティビティを必要とします。これらのオプションは両方とも、たとえ最小限であっても、競合の可能性を高めます。 2つの「コンテキスト」オプションは、軽量/メモリのみである必要があります。

2
Solomon Rutzky