SQL Server:如何仅针对当前会话禁用更新触发?


15

我正在使用SQL Server 2008 R2。

我有一个表好处,它有一个名为tiu_benefit的AFTER INSERT,UPDATE触发器。

我想为此表编写一个UPDATE语句以更新1行,但我不希望其触发器触发。我知道我可以在UPDATE之前禁用触发器,然后在UPDATE之后启用触发器:

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,这不好。这就是为什么我只想为当前会话禁用和启用触发器。可能吗?如果是,请告诉如何。

谢谢


1
如果您无法修改触发器,那么答案是否定的。
jyao

Answers:


6

我对此进行了一些测试,我认为如果您在单个事务中运行您的流程就可以了。

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。然后我打开了一个新的(第二)查询窗口,并试图运行各种DML语句(SELECTINSERTUPDATE DELETE基本表)。所有试图访问第二个查询窗口中的基表的尝试都等待该窗口通过显式事务持有的锁。一旦我提交(或回滚)了我的显式事务,第二个窗口便能够访问该表。


这将起作用,但是锁可能会导致下游意外的问题,具体取决于您保持事务打开的时间。
CaM

@CaM-我假设假设OP快速提交或回滚事务,那么一行更新不会花费太长时间。希望有一个关于benefit_id:) 的索引
Scott Hodgin

我真的很喜欢这个解决方案,因为我不必对触发器进行任何更改
srh18年

18

为了解决您的问题,我们必须采用编程方式解决问题。您可以选择两条路线。之所以需要这些方法,是因为您不能为特定的语句禁用触发器,而只能在整个表中禁用它。

选项1:Context_Info()

Samuel Vanga谈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编程:T-SQL编程》中有一个很好的解决方案,该书也在他的后来的T-SQL Querying中。此context_info函数上的主要问题是较小的TempDB开销。

为了破坏惊喜但又不破坏书籍的情节(我认为它们值得购买和阅读),您将更改触发器。

您的触发器应执行对临时表的检查。如果临时表存在,则触发器应该知道结束并且不执行操作。

在要执行的更新语句中,首先创建临时表。它会在与触发器相同的事务中看到,并且将导致触发器忽略您的语句。

触发器示例:

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

2
我会在触发器中使用context_info()代替temp表。换句话说,如果触发器检测到context_info返回特定值,则触发器将相应地起作用。您可以在此处参考相关的SO问题:stackoverflow.com/questions/3025662/…–
jyao

1
您还可以进行类似于context_info使用的检查,original_login()以告知触发器是否在特定人员点击触发器时永远不会运行。
肯尼斯·费舍尔

2

我会使用CONTEXT_INFO或更新的SESSION_CONTEXT。两者都是基于会话的值。

关于本地临时表选项甚至禁用/启用触发器选项,要考虑的一件事:两者都需要一定数量的锁定和事务日志活动。这两种选择都增加了争用的可能性,即使这种增加很小。两个“上下文”选项应该更轻巧/仅用于内存。


context_info可以缓解您的痛苦,每当您要运行生产数据更改时,这都很方便,尤其是禁用触发器可能会导致其他操作不触发触发器。
Biju jose
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.