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

MS SQL查询中的附加条件正在执行2分钟(与原来的1秒相比),sql,sql-server,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

我有一个不合逻辑的问题,我就是想不出来

我正在做一个复杂的查询。在我做了一点改变后,它开始在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) 
/* ... */
第二次查询

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) 
/* ... */
第一个查询在1秒内执行,但第二个查询需要2分钟以上执行。我就是不明白。两者的区别是什么

和(10001890为空或Cruise.Cruise\u ID=10001890)

和(@CRUISE\u ID为空或CRUISE.CRUISE\u ID=@CRUISE\u ID)

变量
@CRUISE\u ID
在整个查询中没有其他出现


编辑:我在同事和你们的帮助下找到了答案

这是一个很好的解释:

根据传递的参数,最优计划完全不同。优化器不知道这一点,它很安全。它创建了始终有效的计划。这就是为什么在第一个示例中是索引扫描,而不是索引查找的原因之一

我们可以从第二个查询的执行计划中看到,索引scan发生在计划的末尾。我查过了。如果我删除整个条件,则需要2分钟以上的时间执行。

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

鉴于此实例中的x(
Cruise.Cruise\u ID=@Cruise\u ID
)必须为true才能满足AND逻辑,第二个条件(
@Cruise\u ID为null或Cruise.Cruise\u ID=@Cruise\u ID
)可以忽略。因此,确保x是真实的,作为计算的起点

然而,SQL查询优化人员明确地决定,查询计划必须尝试确保满足和的双方,从而按照以下内容对其进行合理化:

仅在条件1的情况下,计划就可以在巡航表上基于(可能已索引的)巡航ID执行(聚集?)索引查找。当您在条件3中添加时,由于必须考虑另一个谓词(
@CruiseID为null
)(
@CRUISE\u ID为null或CRUISE.CRUISE\u ID=@CRUISE\u ID
),优化程序将无法再执行此搜索。因此,必须扫描整个Cruise_行程表(它不能使用其他索引列),然后在将各种条件作为连接的一部分进行检查之前执行到Cruise的连接


本质上,它是按照您的要求执行的—如果该值为NULL,则必须返回所有内容,并对性能造成可预见的灾难性后果。您最好使用IF…ELSE块来确保查询计划针对两种可能的选项进行了优化(@CruiseID为空/不为空)。

为什么要询问“10001890为空”?(或者如果@CRUISE\u ID为null[因为它总是被声明]?),因为它是真实查询中的一个参数-
声明@CRUISE\u ID int=:CRUISE\u ID
。这只是一个测试用例。在查询之前声明变量(如问题中所示)与使用参数时是否得到相同的结果?是否检查了不同查询的查询计划?您好。我想我还不够清楚。我一次只使用一个条件。但你的答案最准确地反映了实际发生的情况。我一直认为查询优化器会知道
@CruiseID的值为null
,并将其视为常量。但我想不是。没那么聪明。