为什么这个非相关查询这么慢?

El *_*oco 2 sql-server query-optimization

我有这个问题......

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
Run Code Online (Sandbox Code Playgroud)

上述查询的执行计划

两个内部区别分别看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)
Run Code Online (Sandbox Code Playgroud)

上述查询的执行计划

然后它需要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)
Run Code Online (Sandbox Code Playgroud)

上述查询的执行计划

然后它会在一秒钟之内重新出现.

什么是SQL Server思考?难道它不能弄清楚它可以执行两个子查询并使用结果作为主题IN.它似乎与相关的子查询一样慢,但它没有相关性!

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

任何人都可以告诉我为什么内部DISTINCTS使这个查询这么慢(但只有当用作IN... 的主题时)?

UPDATE

对不起,我花了一段时间来完成这些执行计划......

查询1

查询2(慢速)

查询3 - 没有内在区别

Pet*_*hia 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 ....
   )
Run Code Online (Sandbox Code Playgroud)

连接两组,并投射独特的元素?原来那里有操作员,它叫做UNION.因此,在计划编译和足够的逻辑转换期间有足够的时间,SQL Server将实现您的真正含义:

select intAttributeID from AST_tblAttributes ....
union
select intAttributeID from AST_tblAttributes ....
Run Code Online (Sandbox Code Playgroud)

但是等等,你把它放在一个相关的子查询中.嗯,相关子查询是半连接,正确的关系不需要在半连接中进行逻辑推理.因此,SQL Server可以在逻辑上重写查询,如下所示:

select * from AST_tblAttributes
where intAttributeID in (
  select intAttributeID from AST_tblAttributes ....
  union all
  select intAttributeID from AST_tblAttributes ....
  )
Run Code Online (Sandbox Code Playgroud)

然后进行物理计划选择.但要实现这一目标,必须首先看一下这个问题,这可能会落在优化窗口之外.


编辑:

实际上,为自己探索这个问题并证实上述推测的方法是将查询的两个版本放在同一个窗口中并且并排比较估计的执行计划(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
Run Code Online (Sandbox Code Playgroud)

输出将显示在消息窗格中.

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