有没有办法只为特定的执行范围禁用SQL Server触发器?

时间:2008-10-06 14:48:39

标签: sql-server tsql triggers

在SQL Server 2005中,有没有办法让触发器找出哪个对象负责触发触发器?我想用它来禁用一个存储的prodecure的触发器。

是否还有其他方法可以仅为当前事务禁用触发器?我可以使用以下代码,但如果我没有弄错,它也会影响并发事务 - 这将是一件坏事。

DISABLE TRIGGER { [ schema_name . ] trigger_name [ ,...n ] | ALL } ON { object_name | DATABASE | ALL SERVER } [ ; ]

ENABLE TRIGGER { [ schema_name . ] trigger_name [ ,...n ] | ALL } ON { object_name | DATABASE | ALL SERVER } [ ; ]

如果可能的话,我想避免在我的表格中使用“NoTrigger”字段并执行NoTrigger = null的技巧,因为我希望保持表格尽可能小。

我想避免触发器的原因是因为它包含对表的手动更新很重要的逻辑,但我的存储过程将处理这个逻辑。因为这将是一个高度使用的程序,我希望它快速。

  

触发器会在服务器上施加额外的开销,因为它们会启动隐式事务。一旦执行了触发器,就会启动一个新的隐式事务,并且事务中的任何数据检索都会在受影响的表上保存锁定。

来自:http://searchsqlserver.techtarget.com/tip/1,289483,sid87_gci1170220,00.html#trigger

11 个答案:

答案 0 :(得分:57)

我刚刚看到这篇文章最近在SQL Server Central新闻简报中突出显示,它似乎提供了一种方法,使用连接上的Context_Info可能会发现它很有用:

http://www.mssqltips.com/tip.asp?tip=1591


Terrapin的编辑:

以上链接包含以下代码:

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)

答案 1 :(得分:6)

如果您的触发器导致应用程序出现性能问题,那么最好的方法是删除对表的所有手动更新,并要求所有更新都通过包含正确更新逻辑的插入/更新存储过程。然后你可以完全移除扳机。

如果没有别的办法,我建议拒绝表更新权限。

这也解决了重复代码的问题。在更新SP和触发器中复制代码违反了良好的软件工程原则,这将是一个维护问题。

答案 2 :(得分:4)

ALTER TABLE tbl DISABLE TRIGGER trg

http://doc.ddart.net/mssql/sql70/aa-az_5.htm

我不明白你的第一段的意思

答案 3 :(得分:2)

由于您指示触发器包含处理所有更新的逻辑,甚至是手动更新,因此应该是逻辑所在的位置。您提到的示例,其中存储过程“将处理此逻辑”意味着重复的代码。此外,如果您想确保每个UPDATE语句都应用此逻辑而不管作者,那么触发器就是它的位置。当某人创作一个程序但忘记再次复制逻辑时会发生什么?在修改逻辑时会发生什么?

答案 4 :(得分:2)

不确定这是不是一个好主意,但似乎对我有用。当触发器被禁用时,事务应该阻止从其他进程插入表。

IF OBJECT_ID('dbo.TriggerTest') IS NOT NULL
 DROP PROCEDURE dbo.TriggerTest
GO

CREATE PROCEDURE [dbo].[TriggerTest]
AS
BEGIN TRANSACTION trnInsertTable1s
;
DISABLE TRIGGER trg_tblTable1_IU ON tblTable1
;
BEGIN -- Procedure Code
    PRINT '@@trancount'
    PRINT @@TRANCOUNT
    -- Do Stuff

END -- Procedure Code
;
ENABLE TRIGGER trg_tblTable1_IU ON tblTable1

IF @@ERROR <> 0 ROLLBACK TRANSACTION
ELSE COMMIT TRANSACTION

答案 5 :(得分:1)

不要禁用触发器。你是正确的,将禁用任何并发事务。

为什么要禁用触发器?它有什么作用?问题是触发问题的原因是什么?从数据完整性角度禁用跳跳器通常是一个坏主意。

答案 6 :(得分:1)

如果性能问题,请考虑重写触发器以提高性能。

答案 7 :(得分:1)

我对这一点感到好意思。一方面,我非常反触发,主要是因为除了问题帖子中链接的文章中所述的原因之外,我还有一个地方可以查找针对我的表执行的代码。

另一方面,如果你有逻辑来强制执行稳定和不可变的业务规则或跨表操作(比如维护历史表)那么将它变成触发器会更安全,所以过程作者和程序员不会需要处理它 - 它只是有效。

所以,我的建议是将必要的逻辑放在你的触发器中,而不是在这一个过程中,这将不可避免地增加到具有相同豁免的几个过程。

答案 8 :(得分:0)

我同意其他一些答案。不要禁用触发器。

这是纯粹的意见,但我避免像瘟疫这样的诱因。我发现很少有使用触发器来强制执行数据库规则的情况。在我的经历中有明显的边缘情况,我只有我的经验来做出这样的陈述。我经常看到用于插入一些关系数据的触发器(应该从业务逻辑中完成),用于将数据插入到报告表中,即对数据进行非规范化(可以通过事务外的进程完成),或者用于转换数据某种程度上来说。

触发器有合法用途,但我认为在日常业务编程中它们很少见。这可能对您当前的问题没有帮助,但您可以考虑完全删除触发器并完成触发器以其他方式执行的工作。

答案 9 :(得分:0)

我刚遇到同样的问题,并提出了以下解决方案,这对我有用。

  1. 创建一个永久数据库表,其中包含您要禁用的每个触发器的一条记录(例如refTriggerManager);每行包含触发器名称(例如strTriggerName ='myTrigger')和位标志(例如blnDisabled,默认为0)。

  2. 在触发器主体的开头,在refTriggerManager中查找strTriggerName ='myTrigger'。如果blnDisabled = 1,则返回而不执行其余的触发代码,否则继续触发代码完成。

  3. 在要禁用触发器的存储过程中,执行以下操作:


  4. BEGIN TRANSACTION

    UPDATE refTriggerManager SET blnDisabled = 1 WHERE strTriggerName ='myTrigger'

    / *更新拥有'myTrigger'的表,但要禁用它。由于refTriggerManager.blnDisabled = 1,'myTrigger'返回而不执行其代码。 * /

    UPDATE refTriggerManager SET blnDisabled = 0 WHERE triggerName ='myTrigger'

    / *触发触发器的可选最终UPDATE代码。由于refTriggerManager.blnDisabled = 0,'myTrigger'完全执行。 * /

    COMMIT TRANSACTION


    所有这些都发生在一个事务中,因此它与外界隔离,不会影响目标表上的其他UPDATE。

    有没有人发现这种方法有任何问题?

    比尔

答案 10 :(得分:0)

您可以使用“ Exec ”功能来禁用和启用存储过程中的触发器。示例:EXEC ('ENABLE TRIGGER dbo.TriggerName on dbo.TriggeredTable')