为什么要检查多个OR条件,即使第一个是真的?

时间:2018-05-22 11:56:22

标签: sql sql-server tsql sql-server-2012

我在条件中有三个 OR

问题是第一个条件变为现实,但它也会检查其他条件并评估最后一个条件。

declare @SearchByParam varchar(20)

set @SearchByParam= 3

Select b.BookingID, ISNULL(Convert(varchar(11),b.AppointmentDate,106),'') as AppointmentDate, isnull(ts.FromTo,'N/A') FromTo, c.CustomerName, c.VehicleRegNo, ISNULL(b.HasCustomerArrived,0) as HasCustomerArrived, ISNULL(b.IsOrderCancelled,0) as IsOrderCancelled 
        from Bookings b 
        inner join Customers c
        on c.CustomerID= b.fk_CustomerID
        left join TimeSlots ts
        ON ts.TimeSlotID= b.fk_TimeSlotID
        where 
        b.BookingID= TRY_CONVERT(int, @SearchByParam) 
        OR
        c.CustomerName like '%'+ @SearchByParam +'%' 
        OR 
        c.VehicleRegNo like '%'+ @SearchByParam +'%' 

请参阅b.BookingID= TRY_CONVERT(int, @SearchByParam)获得3,但它也会评估其他条件。

为什么?它应该在第一个停止。

4 个答案:

答案 0 :(得分:2)

你假设如果它的代数可以短路,那么它应该短路。但这会降低并行性的好处,特别是在使用多行而不是单个标量表达式(例如在C中)时。

为了最大化并行操作,可以生成执行计划,使得短路不会带来任何好处。这就是SQL为declarative而不是imperative的原因;你声明一个问题,然后SQL Server创建计划来解决这个问题。 (命令式语言执行您提供的解决方案。) - 在SQL中,您无法通过更改表达式的顺序来控制已执行操作的顺序。

尝试 强制 的一个选项是将所有三个表达式折叠到单个CASE表达式中,因为这是一个线性标量操作

1 = CASE WHEN b.BookingID = TRY_CONVERT(int, @SearchByParam) THEN 1
         WHEN c.CustomerName like '%'+ @SearchByParam +'%'   THEN 1
         WHEN c.VehicleRegNo like '%'+ @SearchByParam +'%'   THEN 1 END

然而,这极大地限制了规划人员的选择,您可能会发现性能下降。


<强> 编辑:

自从我开始编写这个答案以来,已经阅读了添加的评论,我认为您已经误解了SQL。这不是短路问题。在SQL中,WHERE子句应用于所有其他行的每个输入行

例如,以下内容返回myfield'x''y'的所有行。 会返回'x'所有行,并且只有在'y'没有出现时才会搜索'x'找到了......

WHERE myfield = 'x' OR myfield = 'y'

-- Which is the same as...

WHERE myfield IN ( 'x', 'y' )

在您的情况下,您似乎正在尝试实施动态搜索条件。其中有 许多不良方式 ,并且只有 一些好方法 才能做到这一点。

过于简单&#34;没有好处&#34;方式就是这个......

DECLARE @SearchByParam VARCHAR(20) = '3',
        @SearchByType  INT         =  1

SELECT
    <blah>
WHERE
        (@SearchByType = 1 AND b.BookingID= TRY_CONVERT(int, @SearchByParam))
    OR  (@SearchByTYpe = 2 AND c.CustomerName like '%'+ @SearchByParam +'%' )
    OR  (@SearchByType = 3 AND c.VehicleRegNo like '%'+ @SearchByParam +'%' )

它&#34;不好&#34;因为如果您确实希望按BookingID进行搜索,则会破坏优化程序围绕任何索引构建查询的能力。

对于三个查询,您实际上会更好,每个查询都根据不同的搜索条件进行定制。或者可能是动态SQL,在那里向查询字符串添加必要的WHERE子句,然后执行该字符串。

对于小数据量,上面的示例可能会对您有所帮助。对于较大的数据量,要么使用专用于每个用例的多个查询,要么阅读此(非常深入,但非常有用)文章:http://www.sommarskog.se/dyn-search.html

答案 1 :(得分:0)

您可以将where子句重写为:

    where 
    b.BookingID= TRY_CONVERT(int, @SearchByParam) 
    OR
    (
     c.CustomerName like '%'+ @SearchByParam +'%' and b.BookingID != TRY_CONVERT(int, @SearchByParam)
     OR
     c.VehicleRegNo like '%'+ @SearchByParam +'%' and b.BookingID != TRY_CONVERT(int, @SearchByParam)
    )

答案 2 :(得分:0)

这个问题的最佳解释是我在70-761考试准备书中找到的那个。 SQL是一种声明性语言。这意味着您描述了您想要获得的内容,但是如何将其用于数据库引擎。因为它你不能假设WHERE clasue中的所有状态将从左到右处理。引擎可以选择以您在原始语句中放入的不同顺序来预测谓词。

答案 3 :(得分:0)

从注释和可能的重复答案中可以清楚地看到,编程语言中的短路布尔逻辑在sql server中并不保证,因此您必须使用其他选项,如此。

sql server仍然可以检查所有条件,但它的确会产生与逻辑相同的方式,因此它实际上模拟了短路布尔逻辑,但也确保了sql server无法创建查询计划遗漏了“短路”

declare @SearchByParam varchar(20)

set @SearchByParam= 3

Select b.BookingID, 
       ISNULL(Convert(varchar(11),b.AppointmentDate,106),'') as AppointmentDate, 
       isnull(ts.FromTo,'N/A') FromTo, 
       c.CustomerName, 
       c.VehicleRegNo, 
       ISNULL(b.HasCustomerArrived,0) as HasCustomerArrived,       
       ISNULL(b.IsOrderCancelled,0) as IsOrderCancelled 
from Bookings b 
  inner join Customers c
    on c.CustomerID = b.fk_CustomerID
  left join TimeSlots ts
    ON ts.TimeSlotID = b.fk_TimeSlotID
where b.BookingID = TRY_CONVERT(int, @SearchByParam) 
OR    (b.BookingID <> TRY_CONVERT(int, @SearchByParam) 
       and
       c.CustomerName like '%'+ @SearchByParam +'%' 
      )
OR    (b.BookingID <> TRY_CONVERT(int, @SearchByParam) 
       and
       c.VehicleRegNo like '%'+ @SearchByParam +'%' 
      )