Sql 更新后触发困惑
我正试图让我的头绕过更新后的触发器 目前在我们的数据库中有一个包含游标的触发器。据我所知,触发器中的游标通常表现不佳,因此我正在尝试摆脱游标 当前触发器如下所示:Sql 更新后触发困惑,sql,triggers,sql-server-2008-r2,Sql,Triggers,Sql Server 2008 R2,我正试图让我的头绕过更新后的触发器 目前在我们的数据库中有一个包含游标的触发器。据我所知,触发器中的游标通常表现不佳,因此我正在尝试摆脱游标 当前触发器如下所示: ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement] AFTER INSERT, UPDATE AS BEGIN SET NOCOUNT ON DECLARE @rowcheck int DECLARE @Mov
ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
DECLARE @rowcheck int
DECLARE @MovementID INT
DECLARE @SiteFromID INT
DECLARE @SiteToID INT
DECLARE @SiteResponsibleID INT
DECLARE @FromAddress_Postcode Varchar(20)
DECLARE @ToAddress_Postcode Varchar(20)
DECLARE zcursor CURSOR FOR SELECT ID, SiteFromID, SiteToID, SiteResponsibleID
, FromAddress_Postcode, ToAddress_Postcode FROM inserted
OPEN zcursor
SELECT @rowcheck=1
WHILE @rowcheck=1
BEGIN
FETCH NEXT FROM zcursor INTO @MovementID, @SiteFromID, @SiteToID, @SiteResponsibleID, @FromAddress_Postcode, @ToAddress_Postcode
IF (@@FETCH_STATUS = 0)
BEGIN
UPDATE Tasks_Movement
SET ZoneFromID = dbo.fn_GetZoneFromPostcode(@FromAddress_Postcode),
ZoneToID = dbo.fn_GetZoneFromPostcode(@ToAddress_Postcode)
WHERE Tasks_Movement.ID = @MovementID
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](@SiteFromID)
WHERE Tasks_Movement.ID = @MovementID
AND (@SiteResponsibleID Is NULL OR @SiteResponsibleID=0)
AND (@SiteFromID Is NOT NULL AND @SiteFromID>0)
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](@SiteToID)
WHERE Tasks_Movement.ID = @MovementID
AND (@SiteResponsibleID Is NULL OR @SiteResponsibleID=0)
AND (@SiteToID Is NOT NULL AND @SiteToID>0)
END
ELSE
SELECT @rowcheck=0
END
CLOSE zcursor
DEALLOCATE zcursor
END
ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE tm
SET ZoneFromID = dbo.fn_GetZoneFromPostcode(i.FromAddress_Postcode),
ZoneToID = dbo.fn_GetZoneFromPostcode(i.ToAddress_Postcode)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID;
UPDATE tm
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteFromID)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID
WHERE (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND i.SiteFromID > 0
UPDATE tm
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteToID)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID
WHERE (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND i.SiteToID > 0
END
据我所知,这里的光标完全没有必要(?)
我是否认为以下方法更有效:
ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE Tasks_Movement
SET ZoneFromID = dbo.fn_GetZoneFromPostcode(inserted.FromAddress_Postcode),
ZoneToID = dbo.fn_GetZoneFromPostcode(inserted.ToAddress_Postcode)
FROM inserted
WHERE Tasks_Movement.ID IN (SELECT id FROM inserted)
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](inserted.SiteFromID)
FROM inserted
WHERE Tasks_Movement.ID IN (SELECT id FROM inserted
WHERE (inserted.SiteResponsibleID Is NULL OR inserted.SiteResponsibleID=0)
AND (inserted.SiteFromID Is NOT NULL AND inserted.SiteFromID>0))
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](@SiteToID)
FROM inserted
WHERE Tasks_Movement.ID IN (SELECT id FROM inserted
WHERE (inserted.SiteResponsibleID Is NULL OR inserted.SiteResponsibleID=0)
AND (inserted.SiteToID Is NOT NULL AND inserted.SiteToID>0))
END
我认为你的触发器应该是这样的:
ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
DECLARE @rowcheck int
DECLARE @MovementID INT
DECLARE @SiteFromID INT
DECLARE @SiteToID INT
DECLARE @SiteResponsibleID INT
DECLARE @FromAddress_Postcode Varchar(20)
DECLARE @ToAddress_Postcode Varchar(20)
DECLARE zcursor CURSOR FOR SELECT ID, SiteFromID, SiteToID, SiteResponsibleID
, FromAddress_Postcode, ToAddress_Postcode FROM inserted
OPEN zcursor
SELECT @rowcheck=1
WHILE @rowcheck=1
BEGIN
FETCH NEXT FROM zcursor INTO @MovementID, @SiteFromID, @SiteToID, @SiteResponsibleID, @FromAddress_Postcode, @ToAddress_Postcode
IF (@@FETCH_STATUS = 0)
BEGIN
UPDATE Tasks_Movement
SET ZoneFromID = dbo.fn_GetZoneFromPostcode(@FromAddress_Postcode),
ZoneToID = dbo.fn_GetZoneFromPostcode(@ToAddress_Postcode)
WHERE Tasks_Movement.ID = @MovementID
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](@SiteFromID)
WHERE Tasks_Movement.ID = @MovementID
AND (@SiteResponsibleID Is NULL OR @SiteResponsibleID=0)
AND (@SiteFromID Is NOT NULL AND @SiteFromID>0)
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](@SiteToID)
WHERE Tasks_Movement.ID = @MovementID
AND (@SiteResponsibleID Is NULL OR @SiteResponsibleID=0)
AND (@SiteToID Is NOT NULL AND @SiteToID>0)
END
ELSE
SELECT @rowcheck=0
END
CLOSE zcursor
DEALLOCATE zcursor
END
ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE tm
SET ZoneFromID = dbo.fn_GetZoneFromPostcode(i.FromAddress_Postcode),
ZoneToID = dbo.fn_GetZoneFromPostcode(i.ToAddress_Postcode)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID;
UPDATE tm
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteFromID)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID
WHERE (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND i.SiteFromID > 0
UPDATE tm
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteToID)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID
WHERE (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND i.SiteToID > 0
END
我已将其更改为使用SQl Server的更新。。从
语法,并且在检查站点ID是否大于0时,还删除了冗余的空检查NULL
不大于或小于0,因此,如果SiteID为NULL,SiteID>0永远无法计算为true,因此这是一个冗余的附加检查
最后,我还建议删除用户定义的函数,尽管我看不到这些函数的本质,但从名称来看,它们看起来非常像简单的loukup函数,可以通过连接更有效地实现
编辑 我不使用
UPDATE(column)
函数,而是在更新中添加一个附加连接,以过滤更新的行,例如:
UPDATE tm
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteToID)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID
LEFT JOIN deleted d
ON d.ID = i.ID
WHERE (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND i.SiteToID > 0
AND AND ISNULL(i.SiteToID, 0) != ISNULL(d.SiteToID);
我之所以这样做是因为
UPDATE(siteToID)
将在任何一行有更新值时返回true,因此如果更新1000000行,其中一行有更改,它将对所有行执行更新,而不仅仅是对已更改的行,通过加入deleted
,您可以将更新限制到相关行。我认为您的触发器应该是这样的:
ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
DECLARE @rowcheck int
DECLARE @MovementID INT
DECLARE @SiteFromID INT
DECLARE @SiteToID INT
DECLARE @SiteResponsibleID INT
DECLARE @FromAddress_Postcode Varchar(20)
DECLARE @ToAddress_Postcode Varchar(20)
DECLARE zcursor CURSOR FOR SELECT ID, SiteFromID, SiteToID, SiteResponsibleID
, FromAddress_Postcode, ToAddress_Postcode FROM inserted
OPEN zcursor
SELECT @rowcheck=1
WHILE @rowcheck=1
BEGIN
FETCH NEXT FROM zcursor INTO @MovementID, @SiteFromID, @SiteToID, @SiteResponsibleID, @FromAddress_Postcode, @ToAddress_Postcode
IF (@@FETCH_STATUS = 0)
BEGIN
UPDATE Tasks_Movement
SET ZoneFromID = dbo.fn_GetZoneFromPostcode(@FromAddress_Postcode),
ZoneToID = dbo.fn_GetZoneFromPostcode(@ToAddress_Postcode)
WHERE Tasks_Movement.ID = @MovementID
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](@SiteFromID)
WHERE Tasks_Movement.ID = @MovementID
AND (@SiteResponsibleID Is NULL OR @SiteResponsibleID=0)
AND (@SiteFromID Is NOT NULL AND @SiteFromID>0)
UPDATE Tasks_Movement
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](@SiteToID)
WHERE Tasks_Movement.ID = @MovementID
AND (@SiteResponsibleID Is NULL OR @SiteResponsibleID=0)
AND (@SiteToID Is NOT NULL AND @SiteToID>0)
END
ELSE
SELECT @rowcheck=0
END
CLOSE zcursor
DEALLOCATE zcursor
END
ALTER TRIGGER [dbo].[trg_TaskMovement_Zone] ON [dbo].[Tasks_Movement]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON
UPDATE tm
SET ZoneFromID = dbo.fn_GetZoneFromPostcode(i.FromAddress_Postcode),
ZoneToID = dbo.fn_GetZoneFromPostcode(i.ToAddress_Postcode)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID;
UPDATE tm
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteFromID)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID
WHERE (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND i.SiteFromID > 0
UPDATE tm
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteToID)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID
WHERE (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND i.SiteToID > 0
END
我已将其更改为使用SQl Server的更新。。从
语法,并且在检查站点ID是否大于0时,还删除了冗余的空检查NULL
不大于或小于0,因此,如果SiteID为NULL,SiteID>0永远无法计算为true,因此这是一个冗余的附加检查
最后,我还建议删除用户定义的函数,尽管我看不到这些函数的本质,但从名称来看,它们看起来非常像简单的loukup函数,可以通过连接更有效地实现
编辑 我不使用
UPDATE(column)
函数,而是在更新中添加一个附加连接,以过滤更新的行,例如:
UPDATE tm
SET SiteResponsibleID = [dbo].[fn_GetDefaultDepotResponsibleForSite](i.SiteToID)
FROM Tasks_Movement tm
INNER JOIN inserted i
ON i.ID = tm.ID
LEFT JOIN deleted d
ON d.ID = i.ID
WHERE (i.SiteResponsibleID IS NULL OR i.SiteResponsibleID = 0)
AND i.SiteToID > 0
AND AND ISNULL(i.SiteToID, 0) != ISNULL(d.SiteToID);
我之所以这样做是因为
UPDATE(siteToID)
将在任何一行有更新值时返回true,因此如果更新1000000行,其中一行有更改,它将对所有行执行更新,而不仅仅是对已更改的行,通过加入deleted
,您可以将更新限制在相关行。请告诉我们这是一个什么样的具体数据库系统-许多内容都是特定于供应商的。你在使用MySQL吗?博士后?SQL Server?神谕IBM DB2?完全是别的吗?请更新您的标签以显示您正在使用的数据库系统(以及它的版本!)-谢谢!对不起,马克。。。它是针对SQL Server的。我现在已经更新了标记。当你浏览一大组行时,光标的移动速度很慢。在您的例子中,您只有插入的行,因此删除光标不是问题?不是这样吗?是的,插入的表包含的行数与更新的行数一样多,可能是数百万行,因此游标的速度会很慢。您试图避免像瘟疫一样的游标是正确的,几乎总是有一种基于集合的方法可以超越它,就像所有东西一样,尽管它们有自己的位置。请告诉我们这是什么具体的数据库系统-许多东西是特定于供应商的。你在使用MySQL吗?博士后?SQL Server?神谕IBM DB2?完全是别的吗?请更新您的标签以显示您正在使用的数据库系统(以及它的版本!)-谢谢!对不起,马克。。。它是针对SQL Server的。我现在已经更新了标记。当你浏览一大组行时,光标的移动速度很慢。在您的例子中,您只有插入的行,因此删除光标不是问题?不是这样吗?是的,插入的表包含的行数与更新的行数一样多,可能是数百万行,因此游标的速度会很慢。你试图避免像瘟疫一样的游标是正确的,几乎总是有一种基于集合的方法可以超越它,就像所有东西一样,尽管它们有自己的位置。啊,好吧,现在这更有意义了。谢谢Gareth的建议。我能用以下内容来包装这些更新语句吗?(UPDATE(FromAddress\u Postcode)或UPDATE(ToAddress\u Postcode))BEGIN。。。停止不必要的更新?太棒了。。。谢谢你帮我解决这个问题。现在它变得更有意义了!:)但是,删除时的内部联接是否会阻止以这种方式更新插入的记录?是的,你可以把它改成一个左连接并添加一个where子句(我已经改变了我以前的编辑来显示这个)啊,好的,这现在更有意义了。谢谢Gareth的建议。我能用以下内容来包装这些更新语句吗?(UPDATE(FromAddress\u Postcode)或UPDATE(ToAddress\u Postcode))BEGIN。。。停止不必要的更新?太棒了。。。谢谢你帮我解决这个问题。现在它变得更有意义了!:)但是,删除时的内部联接是否会阻止以这种方式更新插入的记录?IE它只能在纯更新命令上工作,不能在插入命令上工作?是的,可以将其更改为左连接并添加where子句(我已更改了以前的编辑以显示此内容)