在 SQL Server 触发器中处理多行
Posted
技术标签:
【中文标题】在 SQL Server 触发器中处理多行【英文标题】:Handling multiple rows in SQL Server trigger 【发布时间】:2013-09-09 10:02:31 【问题描述】:我们有一个数据库,其中包含一个名为 WarehouseItem 的表,其中保存了产品的库存水平。我需要知道这个表什么时候更新,所以我创建了一个触发器来放置这个更新的表行的主键;到一个单独的表中(如队列系统)。
这是我的触发器:
IF ((SELECT COUNT(*) FROM sys.triggers WHERE name = 'IC_StockUpdate') > 0)
DROP TRIGGER [dbo].[IC_StockUpdate]
GO
CREATE TRIGGER [dbo].[IC_StockUpdate] ON [dbo].[WarehouseItem]
AFTER UPDATE
AS
BEGIN
-- Get Product Id
DECLARE @StockItemID INT = (SELECT ItemID FROM INSERTED);
DECLARE @WarehouseID INT = (SELECT WarehouseID FROM INSERTED);
-- Proceed If This Product Is Syncable
IF (dbo.IC_CanSyncProduct(@StockItemID) = 1)
BEGIN
-- Proceed If This Warehouse Is Syncable
IF (dbo.IC_CanSyncStock(@WarehouseID) = 1)
BEGIN
-- Check If Product Is Synced
IF ((SELECT COUNT(*) FROM IC_ProductCreateQueue WHERE StockItemID = @StockItemID) > 0)
BEGIN
-- Check If Stock Update Queue Entry Already Exists
IF ((SELECT COUNT(*) FROM IC_StockUpdateQueue WHERE StockItemID = @StockItemID) > 0)
BEGIN
-- Reset [StockUpdate] Queue Entry
UPDATE IC_StockUpdateQueue SET Synced = 0
WHERE StockItemID = @StockItemID;
END
ELSE
BEGIN
-- Insert [StockUpdate] Queue Entry
INSERT INTO IC_StockUpdateQueue (StockItemID, Synced) VALUES
(@StockItemID, 0);
END
END
ELSE
BEGIN
-- Insert [ProductCreate] Queue Entry
INSERT INTO IC_ProductCreateQueue (StockItemID, Synced) VALUES
(@StockItemID, 0);
-- Insert [StockUpdate] Queue Entry
INSERT INTO IC_StockUpdateQueue (StockItemID, Synced) VALUES
(@StockItemID, 0);
END
END
END
END
GO
如果“WarehouseItem”表中只更新了一行,这将非常有效。 但是,如果此表中更新了不止一行,我的触发器将无法处理它:
有没有办法在大规模更新事件后迭代“插入”集合?或者如何处理触发器中的多行更新?
【问题讨论】:
以下为解决问题提供了很好的解释,mssqltips.com/sqlservertip/2342/… 我使用以下条件来捕获插入、删除和更新 SELECT @ DELCOUNT = COUNT() FROM DELETED SELECT @ INSCOUNT = COUNT( ) FROM INSERTED IF @DELCOUNT = 0 -- INSERT ,ELSE IF @INSCOUNT = 0 -- DELETE 等 【参考方案1】:你用这个:
-- Get Product Id
DECLARE @StockItemID INT = (SELECT ItemID FROM INSERTED);
DECLARE @WarehouseID INT = (SELECT WarehouseID FROM INSERTED);
但如果您更新多行(作为您的示例),则必须使用不同的策略。
例如,要声明一个变量,请在查询中使用 INSERTED 表在 JOIN 中现在您使用变量的位置。
IF 语句适用于您的变量,但我认为在查询中移动该条件。
尝试以这种方式更改您的 UPDATE 查询(最终添加 IF 条件):
-- Reset [StockUpdate] Queue Entry
UPDATE IC_StockUpdateQueue SET Synced = 0
FROM inserted
WHERE inserted.itemID = StockItemID;
等等。
如需更多信息,请添加评论。
【讨论】:
我想我明白你是从哪里来的。感谢您的提示。我想这就是我需要的。 @LatheesanKanes:好的,如果你愿意,我在这里;) 这对我不起作用,我尝试使用 INSERTED 表加入,但它仍然给我同样的错误,即子查询返回不止一行。我正在尝试使用“UPDATE Table1 SET col1 = col1”之类的语句在一个查询中更新多行,您能帮我解决这个问题吗? @HiteshMistry:我的回答与这个问题有关。请发布您的上下文,以便我可以帮助您。 @Latheesan 你为什么不接受这个作为答案?【参考方案2】:您可以使用循环遍历INSERTED
,但最好将标量变量从INSERTED
更改为TABLE
和INSERT-SELECT
,其中ID 满足前两个IF 的标准
DECLARE @inserted TABLE (StockItemID INT, WarehouseID INT)
INSERT INTO @inserted (StockItemID, WarehouseID)
SELECT StockItemID, WarehouseID
FROM INSERTED i
WHERE dbo.IC_CanSyncProduct(i.StockItemID)=1
AND dbo.IC_CanSyncStock(i.WarehouseID)=1
然后您可以删除 if else upsert 逻辑并使用查询进一步过滤 @inserted
以获取所需的各种更新和插入
;WITH ResetQueueEntry
(
SELECT StockItemID
FROM @inserted i
WHERE EXISTS(SELECT 1 FROM IC_ProductCreateQueue q WHERE q.StockItemID = i.StockItemID)
AND EXISTS(SELECT 1 FROM IC_StockUpdateQueue q WHERE q.StockItemID = i.StockItemID))
)
-- Reset [StockUpdate] Queue Entry
UPDATE IC_StockUpdateQueue
SET Synced = 0
WHERE StockItemID IN (SELECT StockItemID FROM ResetStockUpdate);
WITH InsertQueueEntry
(
SELECT StockItemId, 0 Synced
FROM @inserted
WHERE EXISTS(SELECT 1 FROM IC_ProductCreateQueue q WHERE q.StockItemID = i.StockItemID)
AND NOT EXISTS(SELECT 1 FROM IC_StockUpdateQueue q WHERE q.StockItemID = i.StockItemID))
)
-- Insert [StockUpdate] Queue Entry
INSERT INTO IC_StockUpdateQueue (StockItemID, Synced)
SELECT StockItemID, Synced
FROM InsertQueueEntry
WITH CreateProductEntry
(
SELECT StockItemId, 0 Synced
FROM @inserted
WHERE NOT EXISTS(SELECT 1 FROM IC_ProductCreateQueue q WHERE q.StockItemID = i.StockItemID)
)
-- Insert [ProductCreate] Queue Entry
INSERT INTO IC_ProductCreateQueue (StockItemID, Synced)
SELECT StockItemId, Synced
FROM CreateProductEntry
WITH CreateStockEntry
(
SELECT StockItemId, 0 Synced
FROM @inserted
WHERE NOT EXISTS(SELECT 1 FROM IC_ProductCreateQueue q WHERE q.StockItemID = i.StockItemID)
)
-- Insert [StockUpdate] Queue Entry
INSERT INTO IC_StockUpdateQueue (StockItemID, Synced)
SELECT StockItemId, Synced
FROM CreateProductEntry
【讨论】:
【参考方案3】:如果触发器用于 INSERT, UPDATE 如果记录正在更新并且多个记录受到影响,此代码将退出触发器:
IF (SELECT COUNT(*) FROM Deleted) > 1
BEGIN
Return
END
但如果您想检查 INSERTED 记录集中的每条记录,您可以使用此方法:
DECLARE rstAST CURSOR FOR
SELECT ins.TaskActionId,
_Task.CustomerId,
_AST.ASTQRId,
ins.ExistingQRcode,
ins.NewQRcode
FROM Inserted ins INNER JOIN
dbo.cdn_AST _AST ON ins.ASTId = _AST.ASTId INNER JOIN
dbo.tsk_Task _Task ON ins.TaskId = _Task.TaskId
OPEN rstAST
FETCH NEXT FROM rstAST INTO @TaskActionId, @TaskCustomerId, @ASTQRId, @ExistingQRcode, @NewQRcode
WHILE @@FETCH_STATUS = 0
BEGIN
--use CONTINUE to skip next record or let it traverse the loop
FETCH NEXT FROM rstAST INTO @TaskActionId, @TaskCustomerId, @ASTQRId, @ExistingQRcode, @NewQRcode
END
CLOSE rstAST
DEALLOCATE rstAST
【讨论】:
以上是关于在 SQL Server 触发器中处理多行的主要内容,如果未能解决你的问题,请参考以下文章
sql server触发器 子查询返回值不止一个 如何解决?
SQL Server 在触发器中引发错误“触发器中的事务注定要失败。批处理已中止。”