SQL 服务器 Table 触发器更新聚合 table-update/insert 多条记录
SQL Server Table Triggers update an aggregate table-update/insert multiple records
(注意:我的问题很详细并且特定于我们的系统,所以在我提前回答我的实际问题之前,对于解释的篇幅,我深表歉意。)
我们有许多 table 需要将其中包含的一部分数据聚合到另一个 table。
我已尽力确保触发器可以处理多行事务,而不仅仅是单行事务:
TRIGGER [dbo].[TR_SavedFiles_PlanLibraryMetrics] on [dbo].[SavedFiles]
AFTER INSERT, UPDATE, DELETE
AS
SET NOCOUNT ON
BEGIN
IF EXISTS (
SELECT m.AccountID, m.PlanLibCode
FROM PlanLibraryMetrics AS m
JOIN PlanLibraryItems AS it
ON m.PlanLibCode = it.PlanLibCode
JOIN inserted AS i
ON m.AccountID = i.AccountID
AND (it.ItemTypeID = i.CISFileID AND it.ItemType = 'FILE')
JOIN Accounts AS a ON i.AccountID = a.AccountID
WHERE a.Status = 0
)
BEGIN
UPDATE PlanLibraryMetrics
SET MetricValue = x.newValue
FROM (SELECT COUNT(s.AccountID) AS newValue, it.PlanLibCode, i.AccountID
FROM SavedFiles AS s
JOIN PlanLibraryItems AS it
ON (s.CISFileID = it.ItemTypeID AND it.ItemType = 'FILE')
JOIN inserted AS i
ON s.AccountID = i.AccountID
AND s.CISFileID = i.CISFileID
Group By PlanLibCode, i.AccountID) AS x
JOIN PlanLibraryMetrics AS m
ON x.PlanLibCode = m.PlanLibCode
AND x.AccountID = m.AccountID
END
ELSE IF EXISTS (
SELECT m.AccountID, m.PlanLibCode
FROM PlanLibraryMetrics AS m
JOIN PlanLibraryItems AS it
ON m.PlanLibCode = it.PlanLibCode
JOIN deleted AS d
ON m.AccountID = d.AccountID
AND (it.ItemTypeID = d.CISFileID AND it.ItemType = 'FILE')
JOIN Accounts AS a ON d.AccountID = a.AccountID
WHERE a.Status = 0
)
BEGIN
UPDATE PlanLibraryMetrics
SET MetricValue = x.newValue
FROM (SELECT COUNT(s.AccountID) AS newValue,it.PlanLibCode, d.AccountID
FROM SavedFiles AS s
RIGHT OUTER JOIN deleted AS d
ON s.AccountID = d.AccountID
AND s.CISFileID = d.CISFileID
JOIN PlanLibraryItems AS it
ON (d.CISFileID = it.ItemTypeID AND it.ItemType = 'FILE')
Group By PlanLibCode, d.AccountID) AS x
JOIN PlanLibraryMetrics AS m
ON x.PlanLibCode = m.PlanLibCode
AND x.AccountID = m.AccountID
END
ELSE IF NOT EXISTS (
SELECT m.AccountID, m.PlanLibCode
FROM PlanLibraryMetrics AS m
JOIN PlanLibraryItems AS it ON m.PlanLibCode = it.PlanLibCode
JOIN inserted AS i
ON m.AccountID = i.AccountID
AND (it.ItemTypeID = i.CISFileID AND it.ItemType = 'FILE')
JOIN Accounts AS a ON i.AccountID = a.AccountID
WHERE a.Status = 0
)
BEGIN
INSERT INTO PlanLibraryMetrics
SELECT i.AccountID, it.PlanLibCode, COUNT(s.AccountID)
FROM SavedFiles AS s
JOIN PlanLibraryItems AS it
ON (s.CISFileID = it.ItemTypeID AND it.ItemType = 'FILE')
JOIN inserted AS i
ON s.AccountID = i.AccountID
AND s.CISFileID = i.CISFileID
Group By PlanLibCode, i.AccountID
END
END
它似乎有效,直到我们得到一个系统,我们已经为 "merge" 帐户设置了系统,其中旧帐户的所有记录都将其帐户 ID 更改为主帐户。
我试图完成的行为是更新聚合中的任何现有记录 table 并为主帐户尚不存在的任何内容插入新记录。
所以我的问题是:If exists/elseif exists 子句的存在是否真的使该触发器(以及以该触发器为模型的其余触发器)实际上不处理多行?如果是这样,我认为我的问题不会通过为每个 table 编写 2 或 3 个单独的触发器来解决,因为我仍然必须每次检查现有记录(即 update/delete 触发器和单独的插入触发器)?我是否应该将所有这些移动到存储过程并传入一个 table 变量,该变量包含 inserted/deleted tables 中的所有记录(我想在论坛)?
SQL 不是我的强项,触发更不是。非常感谢更有经验的人提供任何帮助。
简答 - 没有。使用 "if exists" 不会妨碍正确处理多行语句。
但老实说吧。如果您在使用 tsql 和触发器方面遇到困难,那么您应该为每个操作编写单独的触发器 UNTIL 您对自己的方法和逻辑有信心。
您的逻辑似乎过于复杂,但没有人知道您的架构以及您的表是如何使用的。它似乎也至少是一个错误。在您的插入部分,您根据 a.Status = 0 检查是否存在。相关的插入语句中不包含相同的逻辑。这已传播到其他 2 组逻辑。
例如,假设在 SavedFiles 中插入了 5 行。 1 匹配 PlanLibraryMetrics 中的一行,其他不匹配。你的代码是做什么的?执行后,只会更新 PlanLibraryMetrics 中的一行 - 您已跳过 aggregate/insert 其他 4 行所需的逻辑。您的删除逻辑似乎高度怀疑正确的加入;如果不知道各种表格的实际和逻辑键,就很难理解。
关于这一点 - 现在是开始注释您的代码以帮助其他人(包括以后您自己)理解代码应该做什么以及可能为什么这样做的好时机。是的 - 请考虑 Damien 的建议。视图,无论是否编入索引,可能(可能是)一种更好的方法 - 当然也是一种更安全、更易于维护的方法。
(注意:我的问题很详细并且特定于我们的系统,所以在我提前回答我的实际问题之前,对于解释的篇幅,我深表歉意。)
我们有许多 table 需要将其中包含的一部分数据聚合到另一个 table。
我已尽力确保触发器可以处理多行事务,而不仅仅是单行事务:
TRIGGER [dbo].[TR_SavedFiles_PlanLibraryMetrics] on [dbo].[SavedFiles]
AFTER INSERT, UPDATE, DELETE
AS
SET NOCOUNT ON
BEGIN
IF EXISTS (
SELECT m.AccountID, m.PlanLibCode
FROM PlanLibraryMetrics AS m
JOIN PlanLibraryItems AS it
ON m.PlanLibCode = it.PlanLibCode
JOIN inserted AS i
ON m.AccountID = i.AccountID
AND (it.ItemTypeID = i.CISFileID AND it.ItemType = 'FILE')
JOIN Accounts AS a ON i.AccountID = a.AccountID
WHERE a.Status = 0
)
BEGIN
UPDATE PlanLibraryMetrics
SET MetricValue = x.newValue
FROM (SELECT COUNT(s.AccountID) AS newValue, it.PlanLibCode, i.AccountID
FROM SavedFiles AS s
JOIN PlanLibraryItems AS it
ON (s.CISFileID = it.ItemTypeID AND it.ItemType = 'FILE')
JOIN inserted AS i
ON s.AccountID = i.AccountID
AND s.CISFileID = i.CISFileID
Group By PlanLibCode, i.AccountID) AS x
JOIN PlanLibraryMetrics AS m
ON x.PlanLibCode = m.PlanLibCode
AND x.AccountID = m.AccountID
END
ELSE IF EXISTS (
SELECT m.AccountID, m.PlanLibCode
FROM PlanLibraryMetrics AS m
JOIN PlanLibraryItems AS it
ON m.PlanLibCode = it.PlanLibCode
JOIN deleted AS d
ON m.AccountID = d.AccountID
AND (it.ItemTypeID = d.CISFileID AND it.ItemType = 'FILE')
JOIN Accounts AS a ON d.AccountID = a.AccountID
WHERE a.Status = 0
)
BEGIN
UPDATE PlanLibraryMetrics
SET MetricValue = x.newValue
FROM (SELECT COUNT(s.AccountID) AS newValue,it.PlanLibCode, d.AccountID
FROM SavedFiles AS s
RIGHT OUTER JOIN deleted AS d
ON s.AccountID = d.AccountID
AND s.CISFileID = d.CISFileID
JOIN PlanLibraryItems AS it
ON (d.CISFileID = it.ItemTypeID AND it.ItemType = 'FILE')
Group By PlanLibCode, d.AccountID) AS x
JOIN PlanLibraryMetrics AS m
ON x.PlanLibCode = m.PlanLibCode
AND x.AccountID = m.AccountID
END
ELSE IF NOT EXISTS (
SELECT m.AccountID, m.PlanLibCode
FROM PlanLibraryMetrics AS m
JOIN PlanLibraryItems AS it ON m.PlanLibCode = it.PlanLibCode
JOIN inserted AS i
ON m.AccountID = i.AccountID
AND (it.ItemTypeID = i.CISFileID AND it.ItemType = 'FILE')
JOIN Accounts AS a ON i.AccountID = a.AccountID
WHERE a.Status = 0
)
BEGIN
INSERT INTO PlanLibraryMetrics
SELECT i.AccountID, it.PlanLibCode, COUNT(s.AccountID)
FROM SavedFiles AS s
JOIN PlanLibraryItems AS it
ON (s.CISFileID = it.ItemTypeID AND it.ItemType = 'FILE')
JOIN inserted AS i
ON s.AccountID = i.AccountID
AND s.CISFileID = i.CISFileID
Group By PlanLibCode, i.AccountID
END
END
它似乎有效,直到我们得到一个系统,我们已经为 "merge" 帐户设置了系统,其中旧帐户的所有记录都将其帐户 ID 更改为主帐户。
我试图完成的行为是更新聚合中的任何现有记录 table 并为主帐户尚不存在的任何内容插入新记录。
所以我的问题是:If exists/elseif exists 子句的存在是否真的使该触发器(以及以该触发器为模型的其余触发器)实际上不处理多行?如果是这样,我认为我的问题不会通过为每个 table 编写 2 或 3 个单独的触发器来解决,因为我仍然必须每次检查现有记录(即 update/delete 触发器和单独的插入触发器)?我是否应该将所有这些移动到存储过程并传入一个 table 变量,该变量包含 inserted/deleted tables 中的所有记录(我想在论坛)?
SQL 不是我的强项,触发更不是。非常感谢更有经验的人提供任何帮助。
简答 - 没有。使用 "if exists" 不会妨碍正确处理多行语句。
但老实说吧。如果您在使用 tsql 和触发器方面遇到困难,那么您应该为每个操作编写单独的触发器 UNTIL 您对自己的方法和逻辑有信心。
您的逻辑似乎过于复杂,但没有人知道您的架构以及您的表是如何使用的。它似乎也至少是一个错误。在您的插入部分,您根据 a.Status = 0 检查是否存在。相关的插入语句中不包含相同的逻辑。这已传播到其他 2 组逻辑。
例如,假设在 SavedFiles 中插入了 5 行。 1 匹配 PlanLibraryMetrics 中的一行,其他不匹配。你的代码是做什么的?执行后,只会更新 PlanLibraryMetrics 中的一行 - 您已跳过 aggregate/insert 其他 4 行所需的逻辑。您的删除逻辑似乎高度怀疑正确的加入;如果不知道各种表格的实际和逻辑键,就很难理解。
关于这一点 - 现在是开始注释您的代码以帮助其他人(包括以后您自己)理解代码应该做什么以及可能为什么这样做的好时机。是的 - 请考虑 Damien 的建议。视图,无论是否编入索引,可能(可能是)一种更好的方法 - 当然也是一种更安全、更易于维护的方法。