Performance 慢T-SQL触发器
我有两个表“Vector”和“vectorement”。一个向量有许多元素,因此向量和向量元素之间存在外键关系,并带有级联删除。Performance 慢T-SQL触发器,performance,tsql,triggers,Performance,Tsql,Triggers,我有两个表“Vector”和“vectorement”。一个向量有许多元素,因此向量和向量元素之间存在外键关系,并带有级联删除。 Vector有一个字段VectorSize,其中包含VectorElement中相关记录的数量。 显然,这个字段是多余的,但它优化了性能并使查询保持简单,因为我们经常对向量中的元素数量感兴趣。 VectoreElement上有一个触发器,用于更新Vector中的VectorSize字段。当在一个事务中删除或插入多个向量记录时,此触发器会非常慢。 当向量被删除时,级联删
Vector有一个字段VectorSize,其中包含VectorElement中相关记录的数量。
显然,这个字段是多余的,但它优化了性能并使查询保持简单,因为我们经常对向量中的元素数量感兴趣。
VectoreElement上有一个触发器,用于更新Vector中的VectorSize字段。当在一个事务中删除或插入多个向量记录时,此触发器会非常慢。
当向量被删除时,级联删除会删除向量元素,然后触发触发器。现在触发器确实会更新要删除的向量记录,这可能会导致一些问题,但插入也会发生这种情况 触发因素如下:
CREATE TRIGGER [TFact].[AfterDeleteInsertVectorElement]
ON [TFact].[VectorElement]
AFTER DELETE, INSERT
AS
BEGIN
SET NOCOUNT ON;
WITH cteChangedVectors AS
(
SELECT DISTINCT i.VectorId
FROM inserted i
UNION
SELECT DISTINCT i.VectorId
FROM deleted i
)
UPDATE
TFact.Vector
SET
VectorSize = x.size
FROM
Vector v
JOIN
(SELECT VectorId, COUNT(*) as size FROM TFact.VectorElement GROUP BY VectorId) x
ON v.Id = x.VectorId
JOIN cteChangedVectors chg ON chg.VectorId = v.Id
END
SQL看起来很复杂。如果你想要大套,那么分开处理
IF EXISTS (SELECT * FROM DELETED)
UPDATE
V
SET
VectorSize = x.size
FROM
Vector V
JOIN
(SELECT
VectorId, COUNT(*) as size
FROM
DELETED
GROUP BY
VectorId
) x
ON v.Id = x.VectorId
ELSE
UPDATE
V
SET
VectorSize = x.size
FROM
Vector V
JOIN
(SELECT
VectorId, COUNT(*) as size
FROM
INSERTED
GROUP BY
VectorId
) x
ON v.Id = x.VectorId
这样更好吗
UPDATE TFact.Vector
SET VectorSize = x.size
FROM Vector v
inner join (
select VectorId, count(*) size
from TFact.VectorElement
where VectorId in (select VectorId from cteChangedVectors)
group by VectorId
)x on x.VectorId = v.Id
如果数据库最大,请确保在TFact.vectorement.VectorId上有索引
关于GJ,为什么不从插入和删除的表中计算“增量”值,而不是在子表上重新计算整个总和
UPDATE
V
SET
VectorSize = VectorSize + Delta
FROM
Vector V
inner join
(select VectorID,SUM(Deltas) as Delta from
(select VectorID,1 as Deltas from inserted union all
select VectorID,-1 from deleted) t
group by VectorID
) u
on
V.VectorID = u.VectorID
尝试使用索引视图跟踪向量元素的总数 看 SQL Server知道如何高效地跟踪聚合—这比在每次触发器调用时启动一段通用过程代码要便宜 如果您在SQL Server Enterprise上,只需创建视图,查询将被动态重写以使用它们 类似于
CREATE VIEW VectorSize AS
SELECT VectorId, COUNT(*)
FROM Vector NATURAL JOIN VectorElement
GROUP BY VectorId
GO
CREATE UNIQUE CLUSTERED INDEX VectorSizeInd ON VectorSize( VectorId )
SQL Server随后将在数据库中保留向量大小的自动更新“硬拷贝”。不确定这是否发生在现实世界中,但如果通过更新将向量元素从一个向量移动到另一个向量,则此查询不会更新新向量,因为VectoreElement将在触发器中有一个已删除的行和一个已插入的行。@gjvdkamp:触发器定义为“删除后插入”。它当前不处理更新,因此我们将插入或删除其中的行,而不是同时插入或删除这两行。此外,再次读取它,当您插入/删除许多向量时,您可能希望删除触发器,应用更改,重新计算向量大小(可能仅适用于所有向量),然后重新启用触发器。您可以在事务中这样做,以保持数据库对其他用户的一致性。感谢tbarker,这消除了对触发器的需要,从而消除了性能问题。我很好奇,当我们的表增长时,您的解决方案将如何运行。我们预计会有数百万条记录。它应该可以很好地扩展,因为(IIRC)SQL Server知道它只是随着每一行的增加而增加。虽然如果要在一批中批量插入数百万条记录,但可能需要禁用然后重新生成索引。