在subselect中使用变量时,SQL查询需要几分钟

时间:2017-04-28 19:55:49

标签: sql sql-server

我有一个SQL查询,在使用变量而不是硬编码值时需要永远。我找到了一种“更好”的方法来进行此查询,但只是想了解为什么会发生这种情况。

使用变量(1 = @DontNeedToCheckSubSubTable)运行查询时,需要几分钟才能返回。如果我不使用变量(1 = 0),则返回1秒。这是为什么?

-- This query has had the actual table/column names changed
DECLARE @DontNeedToCheckSubSubTable AS BIT;
SET @DontNeedToCheckSubSubTable = 0;

SELECT
    COUNT(*)
FROM
    MainTable MT WITH (NOLOCK)
INNER JOIN 
    SubTable ST WITH (NOLOCK) ON ST.MainTable_ID = MT.ID
WHERE   
    EXISTS (SELECT TOP 1 'x' AS [Exists]
            FROM SubSubTable SST WITH (NOLOCK)
            WHERE 1 = @DontNeedToCheckSubSubTable -- 1 = 0 ????
               OR (SST.MainTable_ID = MT.ID));

编辑: 带变量的执行计划 Execution Plan with Variable 没有变量的执行计划 Execution Plan without Variable

3 个答案:

答案 0 :(得分:3)

OR会抛弃所有优化。将它移到子查询之外:

SELECT COUNT(*)
FROM MainTable MT INNER JOIN
     SubTable ST
     ON ST.MainTable_ID = MT.ID
WHERE (1 = @DontNeedToCheckSubSubTable) OR
      EXISTS (SELECT TOP 1 'x' AS [Exists]
              FROM SubSubTable SST
              WHERE SST.MainTable_ID = MT.ID
             );

应该允许子查询使用SubSubTable(MainTable_ID)上的索引。实际上可能需要union all才能让优化器做正确的事情。

我删除了WITH NOLOCK。除非真的需要,否则这个选项与问题和坏习惯无关。

答案 1 :(得分:1)

戈登的回答改变了语义。

然而,改变的语义可能就是你所需要的。

在原始查询中,即使WHERE EXISTS SubSubTable为空,@DontNeedToCheckSubSubTable = 1也会评为假。在重写版本中不再是这种情况。

您可能会发现,只需在单独的查询中拆分这两种情况即可进一步改进(如果此查询未经常执行,则添加OPTION (RECOMPILE))。这样,SQL Server无需编译适用于任何一种情况的计划。

以下是一个例子。

SQL Server 2016上的Staistics IO结果(逻辑读取)

+---------------------------------+------------+-----------+----------+-----------+
|                                 | spt_values | WorkTable | WorkFile | WorkTable |
+---------------------------------+------------+-----------+----------+-----------+
| @DontNeedToCheckSubSubTable = 0 |            |           |          |           |
| Original                        |        124 |    30,212 |        0 |         0 |
| Gordon's                        |      5,123 |         0 |        0 |           |
| Gordon's + OPTION (RECOMPILE)   |         42 |         0 |        0 |           |
+---------------------------------+------------+-----------+----------+-----------+
| @DontNeedToCheckSubSubTable =1  |            |           |          |           |
| Original                        |         83 |         0 |        0 |         0 |
| Gordon's                        |         28 |         0 |        0 |           |
| Gordon's + OPTION (RECOMPILE)   |         28 |         0 |        0 |           |
+---------------------------------+------------+-----------+----------+-----------+

查询

SELECT COUNT(*)
FROM   master..spt_values MT WITH (NOLOCK)
       INNER JOIN master..spt_values ST WITH (NOLOCK)
         ON ST.number = MT.number
WHERE  EXISTS (SELECT TOP 1 'x' AS [Exists]
               FROM   master..spt_values SST WITH (NOLOCK)
               WHERE  1 = @DontNeedToCheckSubSubTable -- 1 = 0 ????
                       OR ( SST.number = MT.number )); 

答案 2 :(得分:0)

这两个查询的查询输出或性能会有什么不同吗?

一个。

SELECT COUNT(*)
FROM MainTable MT INNER JOIN
    SubTable ST
    ON ST.MainTable_ID = MT.ID
WHERE (1 = @DontNeedToCheckSubSubTable) OR
  EXISTS (SELECT *
          FROM SubSubTable SST
          WHERE SST.MainTable_ID = MT.ID
         );

SELECT COUNT(*)
FROM MainTable MT INNER JOIN
     SubTable ST
     ON ST.MainTable_ID = MT.ID
WHERE (1 = @DontNeedToCheckSubSubTable) OR
  EXISTS (SELECT TOP 1 'x' AS [Exists]
          FROM SubSubTable SST
          WHERE SST.MainTable_ID = MT.ID
         );