我试图让我的头围绕一个AFTER UPDATE触发器。
目前在我们的数据库中有一个包含游标的Trigger。根据我的理解,触发器中的游标通常表现不佳,所以我试图摆脱光标。
目前触发器如下所示:
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 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
答案 0 :(得分:1)
我认为你的触发器应该是这样的:
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的UPDATE .. FROM
语法,并在检查网站ID>时删除了冗余空检查。 0. NULL
不大于或小于0,因此如果SiteID为null,则SiteID> 0永远无法评估为真,因此这是一项冗余的额外检查。
最后,我还建议删除用户定义的函数,虽然我看不到它们的基础,根据名称看起来非常像它们是简单的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,所以如果你更新1,000,000行而其中一行有更改,它将执行更新通过加入deleted
,您可以将更新限制为相关的行,而不仅仅是已更改的那些。