【发布时间】:2016-08-02 16:39:12
【问题描述】:
我正在开发一个 MSSQL 数据库(用于一个名为 Sage 200 的程序)。数据库中有很多表,但是,我希望通过特定表上的触发器来通知更改(插入新记录或更新现有记录)。
我还想支持同时更新此表上的多行。
当插入或更新记录时,我想从表中获取特定字段并使用该字段的值插入/更新另一个表。
所以,把它放在透视图中;触发器如下所示:
CREATE TRIGGER [dbo].[IC_CustomerLocationChanges] ON [dbo].[SLCustomerLocation]
AFTER INSERT,UPDATE
AS
BEGIN
SELECT RowNum = ROW_NUMBER() OVER(ORDER BY SLCustomerAccountID) , SLCustomerAccountID
INTO #CustomerLocationChanges
FROM INSERTED;
DECLARE @MaxRownum INT;
SET @MaxRownum = (SELECT MAX(RowNum) FROM #CustomerLocationChanges);
DECLARE @Iter INT;
SET @Iter = (SELECT MIN(RowNum) FROM #CustomerLocationChanges);
WHILE @Iter <= @MaxRownum
BEGIN
-- Get Customer account Id
DECLARE @SLCustomerAccountID INT = (SELECT SLCustomerAccountID FROM #CustomerLocationChanges WHERE RowNum = @Iter);
-- Check If Customer Doesn't Already Exist In Queue Table
IF ((SELECT COUNT(*) FROM IC_CustomerUpdates WITH (NOLOCK) WHERE SLCustomerAccountID = @SLCustomerAccountID) > 0)
BEGIN
-- Insert new record
print 'Insert [CustomerCreate] Queue Entry | SLCustomerAccountID : ' + CAST(@SLCustomerAccountID AS VARCHAR(255));
INSERT INTO IC_CustomerUpdates (SLCustomerAccountID, Synced) VALUES
(@SLCustomerAccountID, 0);
END
ELSE
BEGIN
-- Update existing record
print 'Update [CustomerCreate] Queue Entry | SLCustomerAccountID : ' + CAST(@SLCustomerAccountID AS VARCHAR(255));
UPDATE IC_CustomerUpdates SET Synced = 0
WHERE SLCustomerAccountID = @SLCustomerAccountID;
END
SET @Iter = @Iter + 1;
END
DROP TABLE #CustomerLocationChanges;
END
GO
为了测试这一点,我运行了以下查询:
update SLCustomerLocation SET AddressLine2 = AddressLine2 + ' test'
where SLCustomerAccountID = 1019
select * from IC_CustomerUpdates
这不会从我的 IC_CustomerUpdates 中返回任何行:
这是我在消息窗口中看到的:
这意味着触发器尚未将记录插入到我的队列表中。有什么想法可能是错的吗?
【问题讨论】:
-
这个条件不是:
IF ((SELECT COUNT(*) FROM IC_CustomerUpdates WITH (NOLOCK) WHERE SLCustomerAccountID = @SLCustomerAccountID) > 0)意味着如果该表中已经存在该客户的行,您将只插入行? -
您可以(并且可能应该)将其重写为基于集合的操作而不是 RBAR。而且确实没有理由将插入的内容放入临时表中。您已经有一个副本,无需创建另一个。小心那些 NOLOCK 提示......它们不仅仅是脏读。 blogs.sqlsentry.com/aaronbertrand/bad-habits-nolock-everywhere
-
@Lamak - 是的,你是对的。那个条件是错误的。应该是:
IF ((SELECT COUNT(*) FROM IC_CustomerUpdates WITH (NOLOCK) WHERE SLCustomerAccountID = @SLCustomerAccountID) = 0)- 刚刚测试过;现在工作正常。很抱歉,愚蠢的错字导致整个触发器不起作用...... -
@SeanLange 我计划将此提交给代码审查以征求意见。我还需要在其他几个表上使用完全相同的触发器,所以不知道如何在不为每个表重复相同的触发器代码的情况下实现这一点。
-
好的,现在可以了。但是有很多地方需要改进。有
SET @Iter = (SELECT MIN(RowNum) FROM #CustomerLocationChanges);之类的细节,你第一次设置这个变量不应该只是SET @Iter = 1吗?无论如何,整个事情都可以改写为基于设置
标签: sql-server triggers database-trigger