为什么这会触发更改我的查询速度?

时间:2010-10-14 13:52:56

标签: sql sql-server sql-server-2005 triggers

我一直在尝试解决一个缓慢的触发问题,现在我已经通过反复试验,我仍然不知道最初的问题是什么。

我正在运行的查询如下:

UPDATE tblA 
SET X = NULL
WHERE X IS NOT NULL AND Z = 0

它会更新大约30k行。

在tblA上导致问题的AFTER INSERT,UPDATE触发器部分是这样的:

IF EXISTS(SELECT 1
          FROM inserted
          LEFT JOIN deleted ON deleted.PK = inserted.PK
          WHERE (inserted.Y IS NOT NULL AND deleted.Y IS NULL)
              OR inserted.Y <> deleted.Y
BEGIN

    -- The above condition is not met for my query so we would never get here
    INSERT INTO tblB
    (...)
    SELECT
    inserted.X,
    ...
    FROM
    inserted
    LEFT JOIN deleted ON deleted.PK = inserted.PK
    WHERE (inserted.Y IS NOT NULL AND deleted.Y IS NULL)
        OR inserted.Y <> deleted.Y

END

我相信上面的IF EXISTS是为了阻止潜在的循环INSERT触发器在没有插入实际发生时触发,但这实际上不是tblB的问题,因为它只有一个触发器。

所以我把它改成了这个:

INSERT INTO tblB
(...)
SELECT
inserted.X,
...
FROM
inserted
LEFT JOIN deleted ON deleted.PK = inserted.PK
WHERE (inserted.Y IS NOT NULL AND deleted.Y IS NULL)
    OR inserted.Y <> deleted.Y

现在更新查询时间已从&gt;下降1小时到30秒左右。

我预计它会花费相同的时间。为什么它更快?

更新:检查使用慢速触发器运行更新查询的执行计划

IF EXISTS检查的成本为0%,其中73%的成本转到另一个触发器语句,该语句将更改插入到审计表中。这本身似乎并不合理,因为该语句在很多连接中非常复杂,但我并不清楚为什么我改写IF EXISTS的改变有所不同。也许我的IF EXISTS检查干扰了审计表插入以某种方式减慢它们,但我不知道为什么新版本不会做同样的事情,因为它包含相同的SELECT。 [这笔费用的大部分用于急切的桌子。]

另外13%的查询成本花费在第三个触发器上,如果特定列值发生更改,则会更新tblA上的时间戳。这再次加入插入和删除,加上tblA。此更新语句对我的查询没有任何影响,因为列X更改不值得更新时间戳。 [此成本在tblA和插入之间的哈希匹配内部联接和聚集索引更新之间分配 - 似乎是合理的。]

添加更多的混淆:如果我禁用触发器,该触发器花费73%的时间但是在没有我的更改的情况下保留上面提到的旧触发器,我的查询仍然需要花费数小时才能运行。我没有尝试禁用时间戳触发器。

使用快速触发时查看查询计划,比率几乎完全相同,但整体时间更短。

2 个答案:

答案 0 :(得分:1)

请调查执行计划,看看每次运行之间有什么区别。我猜SQL-server对exists(...)查询使用的执行计划与insert-select不同,因为它不必覆盖第一种情况下的所有列。如果存在令人困惑的索引或令人困惑的统计数据,优化可能会混淆并选择一个非常糟糕的计划。因此,在调查并保存执行计划后,尝试重新组织/重建所有索引并重新计算该表的统计信息。

问候,Rob

答案 1 :(得分:0)

好吧,我不确定两者之间发生了什么,但我可以为你提供一些提示,以加快它的速度

我要改变的第一件事是:

WHERE (inserted.Y IS NOT NULL AND deleted.Y IS NULL) 

到此:

WHERE (inserted.Y >'' AND deleted.Y IS NULL) 

IS NULL导致索引搜索,其中&gt;''允许sql执行搜索并为您提供相同的结果集(取决于y是否为int,如果它是varchar,那么您可能会更改为&gt; = '')