Почему проверяются множественные ИЛИ условия, даже если 1-е верно? - PullRequest
0 голосов
/ 22 мая 2018

У меня есть Три ИЛИ в состоянии где.

Проблема заключается в том, что 1-е условие выполняется, но оно также проверяет другие условия и оценивает последнее.

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 голосов
/ 22 мая 2018

Как видно из комментариев и возможного дублирующего ответа, логика короткого замыкания, как вы знаете из языков программирования, не гарантируется на сервере sql, поэтому вам придется использовать другие опции, подобные этой.

Все условия могут все еще проверяться сервером sql, но это приводит к тому же самому результату, который вы ожидали от своей логики, поэтому фактически имитирует логическую логику короткого замыкания, но также гарантирует, что сервер sql не может создать план запроса, где "короткое замыкание"схема "не учтена"

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 +'%' 
      )
0 голосов
/ 22 мая 2018

Вы предполагаете, что если алгебраически возможно замкнуть накоротко, то оно должно замкнуть накоротко.Но это исключает преимущества параллелизма, особенно при работе со многими строками, а не с одним скалярным выражением (например, в C) .

Чтобы максимизировать параллельные операции, план выполнения может бытьгенерируется таким образом, что короткое замыкание не дает никакой пользы.Вот почему SQL declarative, а не imperative;Вы объявляете проблему, а затем SQL Server создает план для ее решения. (императивные языки выполняют решение, которое вы даете.) - В SQL вы не можете контролировать порядок выполняемых операций, просто изменяя порядок своих выражений.

Один вариант:попытка force использовать вместо этого все три выражения в одном выражении 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' )

В вашем случае вы, похоже, пытаетесь реализовать условия динамического поиска.Из них есть много плохих способов сделать это, и только несколько хороших способов сделать это ...

Упрощенный способ "ничего хорошего" был бы таким ...

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 +'%' )

Это "не хорошо", потому что если вы хотите выполнить поиск по BookingID, вы уничтожили способность оптимизатора строитьзапрос по любому индексу.

На самом деле вам будет лучше с тремя запросами, каждый из которых соответствует различным критериям поиска.Или, возможно, динамический SQL, где вы добавляете необходимое условие WHERE в строку запроса, а затем выполняете эту строку.

Для небольших объемов данных приведенный выше пример может вам помочь.Для больших объемов данных либо используйте несколько запросов, выделенных для каждого варианта использования, либо прочитайте это (очень подробно, но очень информативно) статья: http://www.sommarskog.se/dyn-search.html

0 голосов
/ 22 мая 2018

Лучшее объяснение этого вопроса - это то, что я нашел в книге подготовки к экзамену 70-761.SQL является декларативным языком.Это означает, что вы описываете, что хотите получить, но как это сделать, оставлено для движка базы данных.Из-за этого вы не можете предполагать, что все определения в классе WHERE будут обрабатываться слева направо.Движок может выбрать элюирование предикатов в ином порядке, который вы поместили в свой первоначальный статус.

0 голосов
/ 22 мая 2018

Вы можете переписать условие 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)
    )
...