我有一个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));
答案 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
);