Оценивается ли короткое замыкание предложения SQL WHERE?

147

Оцениваются ли логические выражения в предложениях SQL WHERE для короткого замыкания ?

Например:

SELECT * 
FROM Table t 
WHERE @key IS NULL OR (@key IS NOT NULL AND @key = t.Key) 

Если @key IS NULL оценивается как истина, оценивается ли @key IS NOT NULL И @key = t.Key ?

Если нет, то почему?

Если да, то гарантируется ли это? Является ли это частью ANSI SQL или зависит от базы данных?

Если конкретная база данных, SqlServer? Оракул? MySQL?

Грег Дин
источник
Разве предложение @key IS NOT NULL не является избыточным? Предложение @key IS NULL на LHS решает эту проблему?
Спендер
10
@splender - зависит от ответа на вопрос
Грег Дин
@Greg: Я согласен с спонсором. Я не вижу никакой разницы в отсутствии или наличии короткого замыкания. Если @key IS NULL, то @key = t.Key всегда будет возвращать false, так как NULL! = NULL (именно поэтому мы используем IS NULL, в конце концов).
Майкл Мэдсен,
14
@Michael и @spender - Суть вопроса в том, оценивается ли второе условие или нет. Вопрос не в том, написан ли этот конкретный оператор SQL как можно меньшим количеством символов. В более сложных примерах это, несомненно, имело бы значение, поскольку при коротком замыкании в предложении where вы могли бы писать выражения, которые в противном случае были бы ошибочными.
Грег Дин,
2
Короткое замыкание подразумевает оценку условий слева направо. При таком условии, как WHERE a = 1 AND b = 2это может быть эффективным для механизма базы данных, сначала найти все строки, где b = 2, а затем отфильтровать, где a = 1. Если вы попросите гарантии, тогда оптимизатор станет бесполезным.
Salman A

Ответы:

73

ANSI SQL Draft 2003 5WD-01-Framework-2003-09.pdf

6.3.3.3 Порядок оценки правил

[...]

Если приоритет не определяется форматами или круглыми скобками, эффективная оценка выражений обычно выполняется слева направо. Однако это зависит от реализации, действительно ли выражения оцениваются слева направо, особенно когда операнды или операторы могут вызывать возникновение условий или если результаты выражений могут быть определены без полной оценки всех частей выражения.

Сообщество
источник
4
Зависит от реализации? Отлично. Это тоже полезно знать. По крайней мере CASE, закорочен.
dakab 07
3
Разве это не означает, что оценки выражений нечетко определены? «(0 = 0 ИЛИ NULL)» всегда имеет значение NULL, если все термины оцениваются, но всегда истинно, если оцениваются слева направо и замыкаются.
user48956
8
SQL - это декларативный язык, он в основном выражает логику вычислений без описания потока управления; что как бы противоречит императивному стилю оценки короткого замыкания и его последствий.
Хорхе Гарсия,
Я не думал об этом так, @JorgeGarcia. Я предполагаю, что оценка короткого замыкания неявно навязывает порядок операций. Я борюсь с некоторым кодом, где это, возможно, лежит в основе тонкой проблемы. Спасибо за понимание.
Карно Антонио Ромеро
59

Из вышесказанного, короткое замыкание на самом деле недоступно.

Если вам это нужно, я предлагаю оператор Case:

Where Case when Expr1 then Expr2 else Expr3 end = desiredResult

Expr1всегда оценивается, но только один из Expr2и Expr3будет оцениваться для каждой строки.

PMc
источник
3
Я полагаю, это зависит от реализации СУБД. По крайней мере, для SQL Server существует по крайней мере одно исключение, которое задокументировано, чтобы не отображать такое поведение (например, короткое замыкание); cf CASE (Transact-SQL) - Примечания . Я процитировал этот случай в этом ответе, который я дал на вопрос Sql - Явный порядок условий WHERE? .
ТТ.
1
Дело выражение , а не утверждение.
jarlh
19

Думаю, это один из тех случаев, когда я бы написал его так, как будто он не замыкался, по трем причинам.

  1. Поскольку для MSSQL это не решается путем взгляда на BOL в очевидном месте, поэтому для меня это делает его канонически неоднозначным.

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

  3. Я пишу достаточно часто для нескольких продуктов СУБД, и я не хочу вспоминать о различиях, если я могу легко их обойти.

dkretz
источник
4
Отличное предложение. Это не отвечает на вопрос, но это отличная прагматическая точка зрения. so +1
Грег Дин
12

Я не верю, что короткое замыкание в SQL Server (2005) гарантировано. SQL Server выполняет ваш запрос через свой алгоритм оптимизации, который принимает во внимание множество вещей (индексы, статистику, размер таблицы, ресурсы и т. Д.), Чтобы разработать эффективный план выполнения. После этой оценки вы не можете с уверенностью сказать, что ваша логика короткого замыкания гарантирована.

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

Мехмет Арас
источник
7

Вы должны помнить, как работают базы данных. Учитывая параметризованный запрос, база данных строит план выполнения на основе этого запроса без значений параметров. Этот запрос используется каждый раз, когда он запускается, независимо от фактических предоставленных значений. Замыкание запроса с определенными значениями не имеет значения для плана выполнения.

Логический разум
источник
6
это важно для скорости исполнения!
user4951 02
То, что это работает в настоящее время, не означает, что его нельзя изменить. Мы должны отделить модель / семантику от реализации. Планы выполнения реализуются внутри для оптимизации выполнения запросов ... и семантика короткого замыкания не только противоречит декларативному характеру SQL, но может ограничивать такую ​​оптимизацию. Однако, если бы семантика оценки короткого замыкания должна была поддерживаться СУБД, реализация планов выполнения изменилась бы для поддержки такой семантики.
Хорхе Гарсия,
3

Я обычно использую это для необязательных параметров. Это то же самое, что короткое замыкание?

SELECT  [blah]
FROM    Emp
WHERE  ((@EmpID = -1) OR (@EmpID = EmpID))

Это дает мне возможность передать -1 или что-то еще, чтобы учесть необязательную проверку атрибута. Иногда это связано с объединением нескольких таблиц или, желательно, представления.

Очень удобно, но не совсем уверен в дополнительной работе, которую он дает движку db.

П. Кэмпбелл
источник
2

Что касается SQL Server, я думаю, это зависит от версии, но мой опыт работы с SQL Server 2000 показывает, что он по-прежнему оценивает @key = t.Key, даже если @key имеет значение null. Другими словами, он не выполняет эффективного короткого замыкания при оценке предложения WHERE.

Я видел, как люди рекомендовали структуру, подобную вашему примеру, как способ выполнения гибкого запроса, при котором пользователь может вводить или не вводить различные критерии. По моим наблюдениям, Key все еще участвует в плане запроса, когда @key имеет значение null, и если Key проиндексирован, он не использует индекс эффективно.

Этот вид гибких запросов с различными критериями, вероятно, является одним из случаев, когда динамически создаваемый SQL - действительно лучший способ. Если @key имеет значение null, вы просто не включаете его в запрос вообще.

тетранц
источник
2

Просто наткнулся на этот вопрос и уже нашел эту запись в блоге: http://rusanu.com/2009/09/13/on-sql-server-boolean-operator-short-circuit/

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

Тем не менее, очевидно, что CASE задокументирован для оценки в письменном порядке - проверьте комментарии к этому сообщению в блоге.

Столсвик
источник
1

Основная характеристика оценки короткого замыкания заключается в том, что она прекращает вычисление выражения, как только может быть определен результат. Это означает, что остальную часть выражения можно игнорировать, потому что результат будет одинаковым независимо от того, оценивается он или нет.

Двоичные логические операторы являются коммутативными, что означает:

a AND b == b AND a
a OR  b == b OR  a
a XOR b == b XOR a

поэтому нет никакой гарантии относительно порядка оценки. Порядок оценки будет определяться оптимизатором запросов.

В языках с объектами могут быть ситуации, когда вы можете писать логические выражения, которые могут быть вычислены только с помощью вычисления короткого замыкания. Конструкция вашего примера кода часто используется на таких языках (C #, Delphi, VB). Например:

if(someString == null | someString.Length == 0 )
  printf("no text in someString");

Этот пример C # вызовет исключение, если someString == nullон будет полностью оценен. При оценке короткого замыкания он будет работать каждый раз.

SQL работает только со скалярными переменными (без объектов), которые нельзя неинициализировать, поэтому нет способа написать логическое выражение, которое нельзя вычислить. Если у вас есть какое-то значение NULL, любое сравнение вернет false.

Это означает, что в SQL вы не можете написать выражение, которое по-разному оценивается в зависимости от использования короткого замыкания или полной оценки.

Если реализация SQL использует оценку короткого замыкания, можно только надеяться, что она ускорит выполнение запроса.

зендар
источник
1
Да, логические операторы коммутативны. Я не думаю, что объекты (или нет) имеют к этому какое-то отношение.
Грег Дин,
1

я не знаю о коротких кругах, но я бы написал это как утверждение if-else

if (@key is null)
begin

     SELECT * 
     FROM Table t 

end
else
begin

     SELECT * 
     FROM Table t 
     WHERE t.Key=@key

end

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

http://en.wikipedia.org/wiki/Sargable

DForck42
источник
1
Может ли кто-нибудь подтвердить это о переменных справа? По некоторым причинам мне трудно в это поверить.
Грег Дин,
searchchoracle.techtarget.com/expert/KnowledgebaseAnswer/ ... больше ничего не могу найти
DForck42
Насколько я понял в статье. Речь идет о функциях с именами столбцов, которые нельзя использовать. Что я понимаю. Однако я не думаю, что (A = @a) или (@a = A) имеет значение.
Грег Дин,
я могу быть не прав. может быть хорошим вопросом, если его еще нет.
DForck42
1

Ниже представлен быстрый и грязный тест на SQL Server 2008 R2:

SELECT *
FROM table
WHERE 1=0
AND (function call to complex operation)

Это немедленно возвращается без записей. Вид короткого замыкания присутствовал.

Затем попробовал это:

SELECT *
FROM table
WHERE (a field from table) < 0
AND (function call to complex operation)

отсутствие записи удовлетворяет этому условию:

(a field from table) < 0

Это заняло несколько секунд, что указывало на то, что поведение короткого замыкания больше не существовало, и сложная операция оценивалась для каждой записи.

Надеюсь, это поможет, ребята.

Хорхе
источник
1
Я предполагаю, что первый запрос был «замкнут» во время компиляции до того, как фактически началось выполнение плана.
Луи Сомерс
1

Вот демонстрация, чтобы доказать, что MySQL действительно выполняет короткое замыкание предложения WHERE :

http://rextester.com/GVE4880

Это запускает следующие запросы:

SELECT myint FROM mytable WHERE myint >= 3 OR myslowfunction('query #1', myint) = 1;
SELECT myint FROM mytable WHERE myslowfunction('query #2', myint) = 1 OR myint >= 3;

Единственная разница между ними - это порядок операндов в условии ИЛИ.

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

myslowfunction called for query #1 with value 1
myslowfunction called for query #1 with value 2
myslowfunction called for query #2 with value 1
myslowfunction called for query #2 with value 2
myslowfunction called for query #2 with value 3
myslowfunction called for query #2 with value 4

Выше показано, что медленная функция выполняется больше раз, когда она появляется слева от условия ИЛИ, когда другой операнд не всегда верен (из-за короткого замыкания).

Стив Чемберс
источник
4
Хм, что вы, вероятно, хотели сказать: «Вот демонстрация, чтобы доказать, что MySQL действительно выполняет короткое замыкание предложения WHERE в этом конкретном случае
TT.
1
Конечно - это просто доказательство того, что такое может случиться.
Стив Чемберс
0

Это занимает дополнительные 4 секунды в анализаторе запросов, поэтому из того, что я вижу, IF даже не закорочен ...

SET @ADate = NULL

IF (@ADate IS NOT NULL)
BEGIN
    INSERT INTO #ABla VALUES (1)
        (SELECT bla from a huge view)
END

Было бы неплохо иметь гарантированный способ!

голубоватый
источник
-2

Но очевидно, что сервер MS Sql поддерживает теорию короткого замыкания, чтобы улучшить производительность, избегая ненужных проверок,

Подтверждающий пример:

SELECT 'TEST'
WHERE 1 = 'A'

SELECT 'TEST'
WHERE 1 = 1 OR 1 = 'A'

Здесь первый пример приведет к ошибке «Ошибка преобразования при преобразовании значения varchar« A »в тип данных int».

В то время как второе выполняется легко, поскольку условие 1 = 1 оценивается как ИСТИНА, и, следовательно, второе условие не выполняется вообще.

Дальше больше

SELECT 'TEST'
WHERE 1 = 0 OR 1 = 'A'

здесь первое условие будет оцениваться как ложное, и, следовательно, СУБД перейдет ко второму условию, и вы снова получите ошибку преобразования, как в примере выше.

ПРИМЕЧАНИЕ. Я НАПИСАЛ ОШИБОЧНОЕ СОСТОЯНИЕ, ЧТОБЫ УЗНАТЬ, ПОГОДА ВЫПОЛНЯЕТСЯ ИЛИ ЗАКОНЧИВАЕТСЯ, ЕСЛИ РЕЗУЛЬТАТ ЗАПРОСА В ОШИБКЕ СОЗДАЕТ, ЧТО СОСТОЯНИЕ ВЫПОЛНЕНО, КОРОТКО ЗАВЕРШЕНО ИНАЧЕ.

ПРОСТОЕ ОБЪЯСНЕНИЕ

Рассмотреть возможность,

WHERE 1 = 1 OR 2 = 2

поскольку первое условие оценивается как ИСТИНА , бессмысленно оценивать второе условие, потому что его оценка в любом значении не повлияет на результат вообще, поэтому для Sql Server это хорошая возможность сэкономить время выполнения запроса, пропустив ненужную проверку или оценку условий .

в случае «ИЛИ», если первое условие оценивается как ИСТИНА, вся цепочка, соединенная «ИЛИ», будет считаться оцененной как истинное без оценки других.

condition1 OR condition2 OR ..... OR conditionN

если условие1 оценивается как истинное, оставьте все условия до тех пор, пока условиеN не будет пропущено. В общих словах при определении первого ИСТИНА все остальные условия, связанные оператором ИЛИ, будут пропущены.

Рассмотрим второе условие

WHERE 1 = 0 AND 1 = 1

поскольку первое условие оценивается как FALSE, бессмысленно оценивать второе условие, потому что его оценка в любом значении не повлияет на результат вообще, поэтому снова это хорошая возможность для Sql Server сэкономить время выполнения запроса, пропустив ненужную проверку или оценку условий .

в случае «И», если первое условие оценивается как ЛОЖЬ, вся цепочка, связанная с «И», будет считаться оцененной как ЛОЖЬ без оценки других.

condition1 AND condition2 AND ..... conditionN

если условие1 оценивается как ЛОЖЬ , оставьте все условия до тех пор, пока условиеN не будет пропущено. В общих словах при определении первого значения FALSE все остальные условия, связанные AND , будут пропущены.

ПОЭТОМУ РАЗУМНЫЙ ПРОГРАММАТОР ДОЛЖЕН ВСЕГДА ПРОГРАММИРОВАТЬ ЦЕПЬ УСЛОВИЙ ТАКИМ ОБРАЗОМ, ЧТОБЫ, МЕНЬШЕ ДОРОГОЕ ИЛИ НАИБОЛЕЕ ИСКЛЮЧАЮЩЕЕ УСЛОВИЕ ПЕРВОЕ ОЦЕНИВАЕТСЯ, ИЛИ СОЗДАТЬ СОСТОЯНИЕ ТАКИМ ОБРАЗОМ, ЧТО МОЖНО ИЗБЕЖАТЬ

RkHirpara
источник
Причина отрицательного голоса: всегда тестируйте вещи на реальном сервере с реалистичными данными. Кажется, мой предыдущий комментарий съел.
Jasmine