MS SQL查询中的附加条件是执行2分钟(与原始1秒相比)

时间:2015-12-22 16:09:04

标签: sql sql-server

我有一个不合逻辑的问题,我无法弄明白。

我正在进行复杂的查询。在我做了一点改变之后,它开始执行超过2分钟而不是一秒钟。有人可以向我解释这怎么可能?可能是什么背景?

首次查询

DECLARE @CRUISE_ID int = 10001890 --:CRUISE_ID
SELECT
/* ... */
FROM Cruise_Itinerary with(nolock)
INNER JOIN Cruise with(nolock) ON Cruise_Itinerary.CRUISE_ID = Cruise.CRUISE_ID 
  AND (Cruise.CRUISE_ID = @CRUISE_ID) 
/* ... */

第二次查询

DECLARE @CRUISE_ID int = 10001890 --:CRUISE_ID
SELECT
/* ... */
FROM Cruise_Itinerary with(nolock)
INNER JOIN Cruise with(nolock) ON Cruise_Itinerary.CRUISE_ID = Cruise.CRUISE_ID 
  AND (@CRUISE_ID is null OR Cruise.CRUISE_ID = @CRUISE_ID) 
/* ... */

第一个查询在一秒钟内执行,但第二个查询需要2分钟才能执行。我只是不明白。

之间有什么区别?

AND (10001890 is null OR Cruise.CRUISE_ID = 10001890)

AND (@CRUISE_ID is null OR Cruise.CRUISE_ID = @CRUISE_ID)

变量@CRUISE_ID没有其他事件发生在整个查询中。

编辑:我在同事和你们的帮助下想出来了。

以下是一个很好的解释: http://sqlinthewild.co.za/index.php/2009/03/19/catch-all-queries/

  

最佳计划完全不同,具体取决于传递的参数。优化器无法分辨它并且它可以安全地运行。它创造了始终有效的计划。这是(原因之一)为什么在第一个例子中它是索引扫描,而不是索引搜索。

我们可以从第二个查询的执行计划中看到,索引扫描发生在计划结束时。我检查了。如果我删除整个条件,则执行需要2分钟以上。 Execution plan for the second query

1 个答案:

答案 0 :(得分:2)

首先,查询中的逻辑似乎是矛盾的。你基本上是在说#34;如果x和(x或y)"。我们(人类)可能会按照以下思路思考:

假设此实例中的x(Cruise.CRUISE_ID = @CRUISE_ID)必须为true才能满足AND逻辑,则可以忽略第二个条件(@CRUISE_ID is null OR Cruise.CRUISE_ID = @CRUISE_ID)。因此,请确保x为真,作为计算的起点。

然而,SQL查询优化器明确地决定查询计划必须尝试确保必须满足AND的两侧,从而使其合理化:

只有条件1,计划可以通过在(可能是索引的)CruiseID的基础上在Cruise表上执行(聚集的?)INDEX SEEK开始。在条件3中添加时,优化器无法再执行此搜索,因为必须考虑(@CruiseID is null)另一个谓词(@CRUISE_ID is null OR Cruise.CRUISE_ID = @CRUISE_ID)。因此,必须扫描整个Cruise_Itinerary表(它没有可以使用的其他索引列),然后在作为连接的一部分检查各种条件之前执行Cruise的连接。

基本上它正在按照你的要求进行操作 - 如果值为NULL,则必须返回所有内容,并对性能产生可预测的破坏性后果。最好使用IF ... ELSE块来确保针对两个可能的选项优化查询计划(@ClaiseID为null /不为null)。