2

我有这个查询...

SELECT Distinct([TargetAttributeID]) FROM
    (SELECT distinct att1.intAttributeID as [TargetAttributeID]
        FROM AST_tblAttributes att1
        INNER JOIN
        AST_lnkProfileDemandAttributes pda
        ON pda.intAttributeID=att1.intAttributeID AND pda.intProfileID = @intProfileID

    union all

    SELECT distinct ca2.intAttributeID as [TargetAttributeID] FROM
        AST_lnkCapturePolicyAttributes ca2
        INNER JOIN
        AST_lnkEmployeeCapture ec2 ON ec2.intAdminCaptureID = ca2.intAdminCaptureID AND ec2.intTeamID = 57
        WHERE ec2.dteCreatedDate >= @cutoffdate) x

上述查询的执行计划

两个内部差异分别查看 32 行和 10,000 行。此查询返回 5 行并在 1 秒内执行。

如果我然后使用此查询的结果作为IN类似的主题......

SELECT attx.intAttributeID,attx.txtAttributeName,attx.txtAttributeLabel,attx.txtType,attx.txtEntity FROM
    AST_tblAttributes attx WHERE attx.intAttributeID 
    IN
    (SELECT Distinct([TargetAttributeID]) FROM
    (SELECT Distinct att1.intAttributeID as [TargetAttributeID]
        FROM AST_tblAttributes att1
        INNER JOIN
        AST_lnkProfileDemandAttributes pda
        ON pda.intAttributeID=att1.intAttributeID AND pda.intProfileID = @intProfileID
    union all
    SELECT  Distinct ca2.intAttributeID as [TargetAttributeID] FROM
        AST_lnkCapturePolicyAttributes ca2
        INNER JOIN
        AST_lnkEmployeeCapture ec2 ON ec2.intAdminCaptureID = ca2.intAdminCaptureID AND ec2.intTeamID = 57
        WHERE ec2.dteCreatedDate >= @cutoffdate) x)

上述查询的执行计划

然后它需要超过3分钟!如果我只是获取查询的结果并执行IN“手动”,那么它会很快返回。

但是,如果我删除两个内部DISTINCTS......

SELECT attx.intAttributeID,attx.txtAttributeName,attx.txtAttributeLabel,attx.txtType,attx.txtEntity FROM
    AST_tblAttributes attx WHERE attx.intAttributeID 
    IN
    (SELECT Distinct([TargetAttributeID]) FROM
    (SELECT att1.intAttributeID as [TargetAttributeID]
        FROM AST_tblAttributes att1
        INNER JOIN
        AST_lnkProfileDemandAttributes pda
        ON pda.intAttributeID=att1.intAttributeID AND pda.intProfileID = @intProfileID
    union all
    SELECT ca2.intAttributeID as [TargetAttributeID] FROM
        AST_lnkCapturePolicyAttributes ca2
        INNER JOIN
        AST_lnkEmployeeCapture ec2 ON ec2.intAdminCaptureID = ca2.intAdminCaptureID AND ec2.intTeamID = 57
        WHERE ec2.dteCreatedDate >= @cutoffdate) x)

上述查询的执行计划

..然后它会在一秒钟内恢复。

SQL Server 在想什么?难道它不知道它可以执行两个子查询并将结果用作IN. 它看起来和相关子查询一样慢,但它不相关!!!

在 Show Estimate Execution 计划中,有三个 Clustered Index Scans,每个扫描的成本为 100%!(执行计划在这里

谁能告诉我为什么内部DISTINCTS使这个查询慢得多(但仅在用作IN...的主题时)?

更新

抱歉,我花了一段时间才制定这些执行计划......

查询 1

查询 2(慢一)

查询 3 - 没有内部差异

4

3 回答 3

9

老实说,我认为归根结底是这样一个事实,就关系运算符而言,您在那里有一个无偿的巴洛克式查询,并且 SQL Server 在允许自己找到替代执行计划的时间内停止搜索替代执行计划。

在计划编译的解析和绑定阶段之后,SQL Server 将对生成的树应用逻辑转换,估计每个的成本,并选择成本最低的一个。它不会穷尽所有可能的转换,只是在给定窗口内可以计算的尽可能多。因此,据推测,它在达到一个好的计划之前已经烧毁了那个窗口,并且它是在 AST_tblAttributes 上添加的外部半自连接将它推到了边缘。

巴洛克风格如何?好吧,首先,有这个(为降噪而简化):

select distinct intAttributeID from (
   select distinct intAttributeID from AST_tblAttributes ....
   union all
   select distinct intAttributeID from AST_tblAttributes ....
   )

连接两组,并投射独特的元素?原来有运营商,它被称为UNION. 因此,如果在计划编译期间有足够的时间和足够的逻辑转换,SQL Server 将意识到您真正的意思是:

select intAttributeID from AST_tblAttributes ....
union
select intAttributeID from AST_tblAttributes ....

但是等等,你把它放在一个相关的子查询中。好吧,相关子查询是半连接,正确的关系不需要在半连接中进行逻辑重复数据删除。因此 SQL Server 可能会在逻辑上将查询重写为:

select * from AST_tblAttributes
where intAttributeID in (
  select intAttributeID from AST_tblAttributes ....
  union all
  select intAttributeID from AST_tblAttributes ....
  )

然后进行物理计划选择。但要做到这一点,它必须先看清楚细节,这可能会超出优化窗口。


编辑:

实际上,自己探索并证实上述推测的方法是将查询的两个版本放在同一个窗口中并并排比较估计的执行计划(SSMS 中的 Ctrl-L)。保留一个,编辑另一个,看看有什么变化。

您将看到一些替代形式被认为是逻辑等效的并生成相同的良好计划,而其他形式则生成不太理想的计划,因为您使用了优化器。**

然后,您可以使用SET STATISTICS IO ONSET STATISTICS TIME ON观察 SQL Server 执行查询的实际工作量:

SET STATISTICS IO ON
SET STATISTICS TIME ON

SELECT ....
SELECT ....

SET STATISTICS IO OFF
SET STATISTICS TIME OFF

输出将出现在消息窗格中。

** 或者不是——如果它们都生成相同的计划,但实际执行时间仍然像你说的那样变化,那么可能会发生其他事情——这并非闻所未闻。尝试比较实际的执行计划并从那里开始。

于 2012-06-12T17:15:01.940 回答
2

埃尔龙诺科

首先是一个可能的解释:

您说:“此查询返回5 行并在 1 秒内执行。”。但是它 ESTIMATE 返回了多少行?如果估计值相差很大,使用查询作为 IN 部分的一部分可能会导致您扫描整个:外部部分中的 AST_tblAttributes,而不是查找它的索引(这可以解释很大的差异)

如果您共享了不同变体的查询计划(请作为文件),我想我应该能够让您了解这里发生了什么。它还可以让我们验证解释。

于 2012-06-14T23:24:21.067 回答
1

编辑:每个 DISTINCT 关键字都会为您的查询计划添加一个新的排序节点。基本上,通过在其中放置其他 DISTINCT,您将迫使 SQL 一次又一次地重新排序整个表,以确保它不会返回重复项。每个这样的操作都可以使查询成本增加四倍。这是对 DISTINCT 运算符可以产生的效果的一个很好的回顾,意料之外的。我自己也被这个咬过。


您使用的是 SQL 2008 吗?如果是这样,您可以试试这个,将 DISTINCT 工作放入 CTE,然后加入您的主表。我发现 CTE 非常快:

WITH DistinctAttribID
AS
(
SELECT Distinct([TargetAttributeID]) 
FROM (
    SELECT distinct att1.intAttributeID as [TargetAttributeID] 
        FROM AST_tblAttributes att1 
        INNER JOIN 
        AST_lnkProfileDemandAttributes pda 
        ON pda.intAttributeID=att1.intAttributeID AND pda.intProfileID = @intProfileID 

    UNION ALL 

    SELECT distinct ca2.intAttributeID as [TargetAttributeID] FROM 
        AST_lnkCapturePolicyAttributes ca2 
        INNER JOIN 
        AST_lnkEmployeeCapture ec2 ON ec2.intAdminCaptureID = ca2.intAdminCaptureID AND ec2.intTeamID = 57 
        WHERE ec2.dteCreatedDate >= @cutoffdate
) x

SELECT attx.intAttributeID,
    attx.txtAttributeName,
    attx.txtAttributeLabel,
    attx.txtType,
    attx.txtEntity 
FROM AST_tblAttributes attx 
JOIN DistinctAttribID attrib
    ON attx.intAttributeID = attrib.TargetAttributeID
于 2012-06-12T00:28:51.383 回答