SQL 服务器 - 在 SP 执行期间中和触发器
SQL Server - Neutralizing a trigger during SP execution
我有两个 table,订单和应用程序。
应用程序是一个 "helper" table,根据订单填充,然后通过网络服务将信息传递到智能手机。
为了填充应用程序,我们创建了一个在特定时间运行的参数化存储过程,将数据从订单流畅地传递到应用程序。
但是这个存储过程没有捕捉到订单的一些更新,所以我们被要求在订单上创建一个触发器,在这些特定的实例中执行这个 SP。这也很好用。
当更新从智能手机到达 table 应用程序时,问题就开始了。相同的参数化 SP 运行 "in reverse" 来更新订单中的字段,这工作得很好——除了这样做会触发我们假定的选择性触发器,从而导致冗余更新。演示:
Orders 中的新行 > SP > Row is written in App > App updated by application > SP > Orders 中的相应行已更新 > Trigger 捕获此更新,再次触发 SP。
在这条链中,只有最后一步有问题。
我已经尝试在 SP 中使用 DISABLE TRIGGER 和 ENABLE TRIGGER 来避免这个问题,但这是有风险的事情,当然不是最好的方法。
我现在正在处理的解决方案是使用 在应用程序更新订单期间更新但在任何其他时间不更新的字段。例如:
UPDATE Orders
SET Orders.StartTime = getdate(),
Orders.EndTime = CASE ... END,
Orders.Unique_Field = X
WHERE Orders.ID = @APPID
在订单的标准更新中,字段 Unique_Field 不包含在任何 INSERT 或 UPDATE 语句中。但是,在App的一些更新中,该字段可能会保持为NULL。
我的问题是:告诉我的触发器忽略来自我的 SP 的任何更新的正确和安全的方法是什么?
目前我的触发器是这样的:
AFTER UPDATE, INSERT
NOT FOR REPLICATION
AS
BEGIN
DECLARE @BUILDORDERCHECK AS DATETIME
DECLARE @ORDERDATECHECK AS DATETIME
DECLARE @ORDERNO AS INT
DECLARE @CHECKER AS TINYINT
SELECT @BUILDORDERCHECK = I.UpdateRecordDate,
@ORDERDATECHECK = I.OrderDate,
@ORDERNO = I.OrderNo,
@CHECKER = CASE WHEN NOT EXISTS (SELECT Unique_Field FROM Inserted) THEN 1 ELSE 0 END
FROM Inserted I
IF @BUILDORDERCHECK IS NOT NULL
AND @ORDERDATECHECK >= dateadd(day,-2,getdate())
AND @CHECKER = 1
-- Does not fire from BuildOrder
-- Does not fire on tasks older than 2 days
BEGIN
EXECUTE [dbo].[Asp_Apper;1] 0, -- CallCode, DO NOT CHANGE
1, -- Auto,
1, -- AOK,
0, -- CancelMsg,
0, -- TrailerNo
1 -- RejectMsg
END
END
@BUILDORDERCHECK 和@ORDERDATECHECK 工作正常并按预期运行,但我需要找到正确的方法来告诉我的触发器检查并查看 Unique_Field 是否包含在更新语句中而不被 NULLS 纠缠.正如我所说,Unique_Field 可以由 SP 更新为 NULL 值,因此简单地检查 NULL 是行不通的。
提前感谢大家的任何想法...
编辑:已经有人指出,这个触发器似乎忽略了更新多行的情况,这是准确的。通常,我们不会像这样构建触发器;但在这种情况下,对订单的更新只会逐行进行,而不会成组进行。唯一不是这种情况的情况是 SP 运行时,无论如何我们都想忽略它。
我会使用 CONTEXT_INFO
and SET CONTEXT_INFO
,像这样:
在触发器中,在顶部添加一个检查,如果设置了特定的上下文值则退出:
IF ISNULL(CONTEXT_INFO(),0x0) = 0x49204C696B6520426967204275747473
RETURN
然后在您想要执行被忽略的操作的(部分)存储过程中,只需设置相同的值:
SET CONTEXT_INFO 0x49204C696B6520426967204275747473;
--Code that shouldn't cause the trigger to fire
SET CONTEXT_INFO 0x0
它很好地包含了内容(与禁用具有 全局 效果的触发器不同)
此外,我知道您已经在评论中声明此触发器只需要 work 进行单行更新,但对我来说这将是代码审查中的自动失败任何未正确处理 inserted
中存在的多行的触发器(或至少 检查 行数并在需要时给出明确的错误消息单行更新尚未完成)
我有两个 table,订单和应用程序。
应用程序是一个 "helper" table,根据订单填充,然后通过网络服务将信息传递到智能手机。
为了填充应用程序,我们创建了一个在特定时间运行的参数化存储过程,将数据从订单流畅地传递到应用程序。
但是这个存储过程没有捕捉到订单的一些更新,所以我们被要求在订单上创建一个触发器,在这些特定的实例中执行这个 SP。这也很好用。
当更新从智能手机到达 table 应用程序时,问题就开始了。相同的参数化 SP 运行 "in reverse" 来更新订单中的字段,这工作得很好——除了这样做会触发我们假定的选择性触发器,从而导致冗余更新。演示:
Orders 中的新行 > SP > Row is written in App > App updated by application > SP > Orders 中的相应行已更新 > Trigger 捕获此更新,再次触发 SP。
在这条链中,只有最后一步有问题。
我已经尝试在 SP 中使用 DISABLE TRIGGER 和 ENABLE TRIGGER 来避免这个问题,但这是有风险的事情,当然不是最好的方法。
我现在正在处理的解决方案是使用 在应用程序更新订单期间更新但在任何其他时间不更新的字段。例如:
UPDATE Orders
SET Orders.StartTime = getdate(),
Orders.EndTime = CASE ... END,
Orders.Unique_Field = X
WHERE Orders.ID = @APPID
在订单的标准更新中,字段 Unique_Field 不包含在任何 INSERT 或 UPDATE 语句中。但是,在App的一些更新中,该字段可能会保持为NULL。
我的问题是:告诉我的触发器忽略来自我的 SP 的任何更新的正确和安全的方法是什么?
目前我的触发器是这样的:
AFTER UPDATE, INSERT
NOT FOR REPLICATION
AS
BEGIN
DECLARE @BUILDORDERCHECK AS DATETIME
DECLARE @ORDERDATECHECK AS DATETIME
DECLARE @ORDERNO AS INT
DECLARE @CHECKER AS TINYINT
SELECT @BUILDORDERCHECK = I.UpdateRecordDate,
@ORDERDATECHECK = I.OrderDate,
@ORDERNO = I.OrderNo,
@CHECKER = CASE WHEN NOT EXISTS (SELECT Unique_Field FROM Inserted) THEN 1 ELSE 0 END
FROM Inserted I
IF @BUILDORDERCHECK IS NOT NULL
AND @ORDERDATECHECK >= dateadd(day,-2,getdate())
AND @CHECKER = 1
-- Does not fire from BuildOrder
-- Does not fire on tasks older than 2 days
BEGIN
EXECUTE [dbo].[Asp_Apper;1] 0, -- CallCode, DO NOT CHANGE
1, -- Auto,
1, -- AOK,
0, -- CancelMsg,
0, -- TrailerNo
1 -- RejectMsg
END
END
@BUILDORDERCHECK 和@ORDERDATECHECK 工作正常并按预期运行,但我需要找到正确的方法来告诉我的触发器检查并查看 Unique_Field 是否包含在更新语句中而不被 NULLS 纠缠.正如我所说,Unique_Field 可以由 SP 更新为 NULL 值,因此简单地检查 NULL 是行不通的。
提前感谢大家的任何想法...
编辑:已经有人指出,这个触发器似乎忽略了更新多行的情况,这是准确的。通常,我们不会像这样构建触发器;但在这种情况下,对订单的更新只会逐行进行,而不会成组进行。唯一不是这种情况的情况是 SP 运行时,无论如何我们都想忽略它。
我会使用 CONTEXT_INFO
and SET CONTEXT_INFO
,像这样:
在触发器中,在顶部添加一个检查,如果设置了特定的上下文值则退出:
IF ISNULL(CONTEXT_INFO(),0x0) = 0x49204C696B6520426967204275747473
RETURN
然后在您想要执行被忽略的操作的(部分)存储过程中,只需设置相同的值:
SET CONTEXT_INFO 0x49204C696B6520426967204275747473;
--Code that shouldn't cause the trigger to fire
SET CONTEXT_INFO 0x0
它很好地包含了内容(与禁用具有 全局 效果的触发器不同)
此外,我知道您已经在评论中声明此触发器只需要 work 进行单行更新,但对我来说这将是代码审查中的自动失败任何未正确处理 inserted
中存在的多行的触发器(或至少 检查 行数并在需要时给出明确的错误消息单行更新尚未完成)