如何绕过SQL Server 2008上的触发器

7

我希望在某些情况下可以跳过触发器,有人能帮我吗?

我尝试使用 这个链接,但没有找到解决方案。

提前感谢。


2
唯一的方法是暂时禁用触发器。如果它存在于表中并且处于活动状态,则无法绕过它。 - marc_s
好的,msrv_s。但如何暂时禁用触发器?你能给我一个例子或一些代码吗?再次感谢。 - MANISHDAN LANGA
2
(1)前往SQL Server的MSDN在线图书,(2)搜索DISABLE TRIGGER - 您将找到此页面,其中提供了您需要的所有详细信息.....禁用:DISABLE TRIGGER Person.uAddress ON Person.Address; 重新启用:ENABLE TRIGGER Person.uAddress ON Person.Address; - marc_s
7个回答

11

禁用触发器是我提出的最后一个解决方案:您无法保证在脚本执行期间没有其他人对表进行更改,因此可能会导致数据丢失或其他不良情况。 - StringBuilder
1
OP 实际上是在问如何绕过他的触发器。正如我在我的答案中提到的那样,要谨慎使用。 - Ralf de Kleine

10

你不能避免触发器被运行。 你可以在其中添加条件,例如:

CREATE TRIGGER trigger_name
   ON table
   AFTER INSERT 
AS
begin
   IF (your condition) begin
     --code
   END
end

如果你有一个INSTEAD OF触发器,请小心。如果你没有编写INSERT代码,表中将不会插入任何内容。


谢谢@diengo,但是我有两个表tableA和tableB,我的触发器无法处理多条记录的更新。所以现在根据要求,我需要更新大约100行tableA,这样只有一条记录会更新tableB(也就是说只触发一次触发器)。在这种情况下,我想绕过触发器,我们将为tableA和tableB编写两个更新语句。谢谢。 - MANISHDAN LANGA
嗨,Manish! 我不明白。你能具体一些吗?也许提供一些例子。 - Diego
Table_A(ID,Name,Date) Table_B(BID,Name)我们在Table_A上编写了Update触发器,用于更新Table_B。请参见以下情况: 当执行以下语句时:Update Table_A set Name ='OB' where ID in(1,2,3,4,5,6,7,...100) 触发器只会被激活一次。 - MANISHDAN LANGA
抱歉,伙计,我还是没明白 :( - Diego

6

您可以通过检查临时表的存在来抑制触发器。需要抑制触发器的代码应该创建一个临时表(比如#suppress_trigger)。在您的触发器中检查是否存在这个临时表并返回。 例如:

CREATE TABLE [dbo].[dummy](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Val] [char](1) NULL)   

--create a history table which gets populated through trigger
CREATE TABLE [dbo].[dummy_hist](
[Id] [int] NULL,
[Val] [char](1) NULL) 

CREATE TRIGGER [dbo].[trig_Insert]
   ON  [dbo].[dummy]    
   AFTER INSERT
AS 
BEGIN

    SET NOCOUNT ON;
    if OBJECT_ID('tempdb..#Dummy_escape_trig') is not NULL
        RETURN

    INSERT INTO dummy_hist
    SELECT * FROM inserted

END

--Proc for which trigger needs to be suppressed
CREATE PROCEDURE [dbo].[ins_dummy]
        @val AS CHAR(1)
AS
BEGIN

    SET NOCOUNT ON;    

    CREATE TABLE #Dummy_escape_trig (id int)

INSERT INTO dummy
    VALUES(@val)
END

如果您在触发器中实际上需要一个临时表,那么这个解决方案特别明显! - StringBuilder

4

@Manish:我认为从最佳实践的角度来看,绕过触发器并不是一个好选择。相反,我会评估、考虑并筛选出触发器触发所需的一组条件。


0

在生产环境中禁用触发器存在一些实际问题。我假设您将特别禁用表触发器(而不是数据库或服务器范围的触发器):

  1. 您需要对所操作的表具有ALTER权限。出于安全原因,这通常被认为是有问题的。在数据库和服务器级别上变得更加棘手。
  2. 禁用不仅限于您特定的连接/会话,而且将影响所有触发器触发的事件,无论从哪个会话,只要它被禁用。如果您希望代码并发执行,则必须假定同一代码将从其他线程调用。之前的建议中缺少使用任何类型的信号量所需的并发管理的全面考虑。特别是,该临时表为代码的受保护部分(在多线程意义上)定义了一个信号量,其中信号量是临时表存在的事实。如果您真正禁用触发器,则为了正确使用该临时表进行其预期目的,必须在受保护代码创建临时表之前进行测试,以查看它是否已存在。这可能需要一个全局临时表,或者您可以更聪明地搜索tempdb以查看它是否存在于任何会话中。受保护的代码必须测试信号量是否存在,并进入阻塞状态,如果它打算等待资源(在本例中为触发器)按预期可用。这变得复杂,并且与复杂性相比几乎没有价值。

观点:虽然禁用/启用触发器很容易,但如果您在某些特定用例中暂时禁用它们作为并发管理策略的一部分,则需要注意许多考虑因素。

除非您对并发管理的细节相对熟悉,或者除非您绝对不需要代码可重入/并发,否则如果您使用DISABLE TRIGGER而不考虑这些因素,您将(可能是零星的)遇到问题。

考虑到所有情况,我能想到的最安全的方法是不禁用触发器,使用本地临时表作为只影响当前会话的信号量,仔细编写退出代码,以确保在受保护的代码结束时临时表被彻底销毁,正如已经建议的那样,但它仍然需要一个检查/阻止,即使只有在同一连接上,并且特别是在同一连接上并行执行时才会有影响。这种情况下绝对最安全的方法是为受保护的代码单独创建一个存储过程。该存储过程进行检查/阻止,然后如果它继续执行(在阻塞代码退出后,您需要检查死锁错误),则创建临时表。由于临时表在存储过程返回时被销毁,因此从受保护的代码中任何路径都将处理信号量。但是,临时表在整个会话中都可用 - 不仅在存储过程内部(当存储过程正在运行时),甚至不仅在批处理中。SQL Server支持单个会话上的并行查询,因此在会话的一个线程中创建的临时表在任何其他线程中都是可见的。这意味着它可以在存储过程之外,在同一会话中看到,实际上,此时可以运行相同的代码。这就是为什么在这种情况下仍然需要真正的并发管理。

最后,我为这个复杂的评论道歉。我发现几乎每次关于多线程和并发管理的讨论都会变成这样,因为虽然这些概念并不是很难,但编码实践长期以来一直被认为是微妙而脆弱的,并容易出现开发人员错误。

0
创建一个不同的用户。在触发器内部检查当前用户并执行相应操作。

0
另一个选项是在连接字符串中包含 Application Name=MyAppName;,并在触发器中添加一个条件,使用 SQL APP_NAME 函数。
IF(APP_NAME() = 'MyAppName')

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接