Performance 慢T-SQL触发器

Performance 慢T-SQL触发器,performance,tsql,triggers,Performance,Tsql,Triggers,我有两个表“Vector”和“vectorement”。一个向量有许多元素,因此向量和向量元素之间存在外键关系,并带有级联删除。 Vector有一个字段VectorSize,其中包含VectorElement中相关记录的数量。 显然,这个字段是多余的,但它优化了性能并使查询保持简单,因为我们经常对向量中的元素数量感兴趣。 VectoreElement上有一个触发器,用于更新Vector中的VectorSize字段。当在一个事务中删除或插入多个向量记录时,此触发器会非常慢。 当向量被删除时,级联删

我有两个表“Vector”和“vectorement”。一个向量有许多元素,因此向量和向量元素之间存在外键关系,并带有级联删除。
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知道它只是随着每一行的增加而增加。虽然如果要在一批中批量插入数百万条记录,但可能需要禁用然后重新生成索引。