Чи оцінюється коротке замикання пункту SQL WHERE?


142

Чи оцінюються булеві вирази в коротких замиканнях пунктів SQL WHERE ?

Наприклад:

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

Якщо @key IS NULL оцінюється як true, чи @key НЕ NULL І @key = t.Key оцінюється?

Якщо ні, то чому б ні?

Якщо так, це гарантується? Це частина ANSI SQL чи це конкретна база даних?

Якщо конкретна база даних, SqlServer? Oracle? MySQL?


Чи не застереження @key НЕ NULL зайвим? Стаття @key IS NULL про LHS бере на себе це питання ні?
витрачається

10
@splender - залежить від відповіді на запитання
Greg Dean

@Greg: Я погоджуюся з витратником. Я не бачу, щоб відсутність чи наявність короткого замикання мали значення. Якщо @key IS NULL, то @key = t.Key завжди повертатиме false, як NULL! = NULL (саме тому ми використовуємо IS NULL).
Майкл Мадсен

14
@Michael і @spender - Суть питання в тому, чи оцінює друга умова чи ні. Суть питання не в тому, чи є ця специфічна операція SQL написана якомога менше символів. У більш складних прикладах це, безсумнівно, має значення, як якщо б коротке замикання з пунктом де, ви могли б написати вираз, який інакше був би помилковим.
Грег Дін

2
Коротке замикання передбачає оцінку умов зліва направо. Враховуючи таку умову, як WHERE a = 1 AND b = 2це може бути ефективним для того, щоб двигун бази даних знаходив спочатку всі рядки, де b = 2, а потім фільтрувати, де a = 1. Якщо ви попросите гарантії, оптимізатор стає марним.
Салман А

Відповіді:


72

Проект ANSI SQL 2003 5WD-01-Framework-2003-09.pdf

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

[...]

Якщо пріоритет не визначається форматами або дужками, ефективне оцінювання виразів, як правило, проводиться зліва направо. Однак це залежить від реалізації, чи дійсно вирази оцінюються зліва направо, особливо коли операнди або оператори можуть спричинити підвищення умов або якщо результати виразів можна визначити без повної оцінки всіх частин виразу.


4
Залежно від реалізації? Чудово. Добре знати також. Принаймні CASEкоротке замикання.
дакаб

3
Чи це не означає, що оцінки виразів не визначені? "(0 = 0 АБО NULL)", завжди NULL, якщо оцінюються всі терміни, але завжди вірно, якщо оцінюється зліва направо і коротко замикається.
user48956

7
SQL - це декларативна мова, вона в основному виражає логіку обчислень, не описуючи її потік управління; який суперечить імперативному стилю оцінки короткого замикання та його наслідкам.
Хорхе Гарсія

Я не думав про це так @JorgeGarcia. Я думаю, що оцінка короткого замикання неявно змушує наказ про операції. Я борюся з деяким кодом, де це, можливо, в корені тонкої проблеми. Дякуємо за розуміння.
Карно Антоніо Ромеро

58

З вищезазначеного коротке замикання насправді недоступне.

Якщо вам це потрібно, я пропоную заяву:

Where Case when Expr1 then Expr2 else Expr3 end = desiredResult

Expr1завжди оцінюється, але лише один з них Expr2і Expr3буде оцінюватися за рядком.


3
Від цього залежить реалізація RDBMS. Щонайменше для SQL Server існує принаймні один виняток, який задокументований, щоб не відображати подібну поведінку (тобто коротке замикання); cf CASE (Transact-SQL) - зауваження . Я цитував цей випадок у цій відповіді, яку я дав на запитання Sql - Явний порядок ДЛЯ умов? .
ТТ.

1
Вираз справи , а не твердження.
jarlh

19

Я думаю, що це один із випадків, коли я б написав це так, ніби він не короткий замикач, з трьох причин.

  1. Оскільки для MSSQL це не вирішено, дивлячись на BOL в очевидному місці, так що для мене це робить канонічно неоднозначним.

  2. бо принаймні тоді я знаю, що мій код спрацює. І що ще важливіше, так будуть і ті, хто йде за мною, тому я не налаштовую їх турбуватися через те саме питання знову і знову.

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


4
Чудова пропозиція. Це не відповідає на питання, але це чудова прагматична точка зору. так +1
Грег Дін

12

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

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


7

Ви повинні мати на увазі, як працюють бази даних. Враховуючи параметризований запит, db будує план виконання на основі цього запиту без значень для параметрів. Цей запит використовується щоразу, коли запит виконується незалежно від того, які фактично надані значення. Чи не буде мати значення запит короткого замикання з певними значеннями для плану виконання.


6
це важливо для швидкості виконання!
user4951

Тільки тому, що це те, що він працює зараз, не означає, що його неможливо змінити. Ми маємо відокремити модель / семантику від реалізації. Плани виконання реалізуються внутрішньо для оптимізації виконання запитів ..., а семантика короткого замикання не тільки суперечить декларативному характеру SQL, але може стримувати такі оптимізації. Однак якщо семантика оцінки короткого замикання повинна підтримуватися СУБД, реалізація планів виконання буде змінена на підтримку такої семантики.
Хорхе Гарсія

3

Я зазвичай використовую це для необов'язкових параметрів. Це те саме, що коротке замикання?

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

Це дає мені можливість пройти через -1 або що завгодно для обліку необов'язкової перевірки атрибута. Іноді це передбачає об'єднання на декілька таблиць, або, бажано, на вигляд.

Дуже зручно, не зовсім впевнений у додатковій роботі, яку він надає db двигуну.


2

Для SQL Server, я думаю, це залежить від версії, але мій досвід роботи з SQL Server 2000 полягає в тому, що він все ще оцінює @key = t.Key, навіть коли @key недійсний. Іншими словами, це не робить ефективного короткого замикання при оцінці пункту WHERE.

Я бачив, як люди рекомендують таку структуру, як ваш приклад, як спосіб робити гнучкий запит, коли користувач може вводити чи не вводити різні критерії. Моє зауваження полягає в тому, що Key все ще бере участь у плані запитів, коли @key є нульовим, а якщо Key індексовано, він не використовує індекс ефективно.

Цей різновид гнучких запитів із різними критеріями, мабуть, є одним із випадків, коли динамічно створений SQL - це справді найкращий спосіб. Якщо @key недійсний, ви просто не включаєте його до запиту.


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 значення, будь-яке порівняння повернеться хибним.

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

Якщо реалізація SQL використовує оцінку короткого замикання, це може лише сподіватися прискорити виконання запитів.


1
Так, булеві оператори є комутаційними. Я не думаю, що об’єкти (чи ні) не мають нічого спільного з цим.
Грег Дін

1

Я не знаю про коротке циркуляцію, але я б написав це як твердження як-небудь

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


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 секунди в аналізаторі запитів, так що з того, що я бачу, якщо це навіть не коротко ...

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 заощадити час виконання запиту, пропустивши перевірку непотрібних умов або оцінку .

у випадку "АБО", якщо перша умова оцінюється ІСТИНА, весь ланцюг, з'єднаний "АБО" , вважатиметься оціненим істинним без оцінки інших.

condition1 OR condition2 OR ..... OR conditionN

якщо умова1 оцінюється як істинна, всі умови, поки умоваN не буде пропущена Узагальнених словах при визначенні першої ПРАВИЛЬНОЇ , всі інші умови, пов'язані АБО, будуть пропущені.

Розглянемо другу умову

WHERE 1 = 0 AND 1 = 1

як перша умова стає evalutated в БРЕХНЯ його безглуздо оцінювати друга умова , оскільки його оцінка в будь-якому значенні , не впливає на результат на всіх, так що знову його хорошу можливість для Sql Server , щоб зберегти час виконання запитів, пропускаючи непотрібні перевірки стану або оцінки .

у випадку "AND", якщо перша умова оцінюється на FALSE, весь ланцюг, пов'язаний з "AND" , вважатиметься оціненим FALSE без оцінки інших.

condition1 AND condition2 AND ..... conditionN

якщо умова1 оцінено на ЛІЖНУ , залиште всі умови, поки умоваN не буде пропущена. Узагальнених словах при визначенні першого ЛЖУ , всі інші умови, пов'язані І , будуть пропущені.

НИХ, мудра програміст повинні ЗАВЖДИ ПРОГРАМА ЦЕПЬ УМОВ таким чином, що менш дорогий або більшість дискваліфікують УМОВИ отримує оцінку перші, або аранжують УМОВИ таким чином, який може приймати максимальну користь від короткого замикання


Причина зворотного переказу: завжди перевіряйте речі на реальному сервері з реалістичними даними. Здається, мій попередній коментар з'їли.
Жасмін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.