SQL и логические операторы и проверки null
у меня есть смутная, возможно, грузовая память за годы работы с SQL Server, что, когда у вас есть столбец possibly-null, небезопасно писать предикаты предложения "WHERE", такие как:
... WHERE the_column IS NULL OR the_column < 10 ...
это было связано с тем, что правила SQL не предусматривают короткого замыкания (и на самом деле это плохая идея, возможно, по причинам оптимизации запросов), и, таким образом, сравнение " почему это было бы ужасно, я не знаю, но я помню, что меня строго предупреждали некоторые документы, чтобы всегда кодировать это как предложение "CASE":
... WHERE 1 = CASE WHEN the_column IS NULL THEN 1 WHEN the_column < 10 THEN 1 ELSE 0 END ...
(глупая часть "1 =" заключается в том, что SQL Server не имеет/не имел первоклассных логических значений, или, по крайней мере, я думал, что это не так.)
Итак, мои вопросы здесь:
- это действительно верно для SQL Server (или, возможно, back-rev SQL Server 2000 или 2005) или я просто спятил?
- если так же нюанс применить к PostgreSQL? (8.4 если это важно)
- в чем именно проблема? Это связано с тем, как работают индексы или что-то еще?
мое заземление в SQL довольно слабое.
6 ответов
я не знаю SQL Server, поэтому я не могу говорить с этим.
дается выражением a L b
для некоторого логического оператора L
, нет никакой гарантии, что a
будет оцениваться до или после b
или даже, что оба a
и b
будут оцениваться:
порядок вычисления подвыражений не определен. В частности, входы оператора или функции не обязательно оценивается слева направо или в любом другом фиксированном порядке.
кроме того, если результат выражения может быть определен путем оценки только некоторых его частей, то другие выражения не может быть оценена.
[...]
Обратите внимание, что это не то же самое, что "короткое замыкание" слева направо булевых операторов, которое встречается в некоторых языках программирования.как следствие, неразумно использовать функции со стороной эффекты как часть сложных выражений. Особенно опасно полагаться на побочные эффекты или порядок оценки в
WHERE
иHAVING
предложения, поскольку эти предложения широко перерабатываются как часть разработки плана выполнения.
что касается выражения формы:
the_column IS NULL OR the_column < 10
обеспокоен, не о чем беспокоиться, так как NULL < n
is NULL
для всех n
, даже NULL < NULL
оценивает в NULL
; кроме того, NULL
неправда так что
null is null or null < 10
это просто сложный способ сказать true or null
и true
независимо от того, какое под-выражение вычисляется первым.
все "использовать случай" звучит в основном как cargo-cult SQL для меня. Однако, как и большинство карго-культистов, под грузом есть ядро истины; чуть ниже моего первого отрывка из руководства PostgreSQL вы найдете следующее:
когда необходимо заставить порядок оценки, A
CASE
строительство (см. раздел 9.16) можно использовать. Например, это ненадежный способ избежать деления на ноль вWHERE
статья:SELECT ... WHERE x > 0 AND y/x > 1.5;
но это безопасно:
SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
Итак, если вам нужно защитить от условия, которое вызовет исключение или будет иметь другие побочные эффекты, то вы должны использовать CASE
контролировать порядок оценки как CASE
is оценивается по порядку:
каждый условие - это выражение, которое возвращает
boolean
результат. Если результат условия равен true, значениеCASE
выражение результат это следует за условием, а остальная частьCASE
выражение не обрабатывается. Если результат условия неверен, любые последующие предложения WHEN рассматриваются таким же образом.
Итак, учитывая это:
case when A then Ra
when B then Rb
when C then Rc
...
A
is гарантированно оценивается до B
, B
до C
, etc. и оценка останавливается, как только одно из условий принимает значение true.
в общем, a CASE
короткие замыкания, но ни AND
, ни OR
короткое замыкание, поэтому вам нужно использовать только CASE
когда вам нужно защитить от побочных эффектов.
Я никогда не слышал о такой проблеме, и этот бит документации SQL Server 2000 использует WHERE advance < 00 OR advance IS NULL
в Примере, поэтому это не должно было быть очень строгим правилом. Моя единственная забота о OR
это то, что он имеет более низкий приоритет, чем AND
, поэтому вы можете случайно написать что-то вроде WHERE the_column IS NULL OR the_column < 10 AND the_other_column > 20
когда это не то, что вы имеете в виду; но обычным решением являются скобки, а не большой CASE
выражение.
Я думаю, что в большинстве RDBMSes индексы не включают null значения, поэтому индекс the_column
не было бы очень полезно для этого запроса; но даже если бы это было не так, я не понимаю, почему большая CASE
выражение было бы более удобным для индекса.
(конечно, это трудно доказать, и, возможно, кто-то будет знать, что вы имеете в виду?)
Ну, я неоднократно писал запросы, как первый пример, так как примерно навсегда (черт, я написал генераторы запросов, которые генерируют такие запросы), и у меня никогда не было проблем.
Я думаю, вы, возможно, помните некоторые увещевания кто-то дал вам когда-то против написания фанки присоединиться условия чтобы использовать OR
. В первом примере условия, к которым присоединяется OR
ограничьте один и тот же столбец одной и той же таблицы, что нормально. Если ваш второй условие было условием соединения (т. е. оно ограничивало столбцы из двух разных таблиц), тогда вы могли попасть в плохие ситуации, когда планировщик запросов просто не имеет выбора, кроме как использовать Декартовое соединение (плохо, плохо, плохо!!!).
Я не думаю, что ваша функция CASE действительно что-то там делает, за исключением, возможно, затрудняет попытки вашего планировщика запросов найти хороший план выполнения для запроса.
но в более общем плане, просто сначала напишите простой запрос и посмотрите, как он выполняет для реалистичных данных. Нет необходимости беспокоиться о проблеме, которая может даже не существовать!
вместо
the_column IS NULL OR the_column < 10
Я бы сделал
isnull(the_column,0) < 10
или для первого примера
WHERE 1 = CASE WHEN isnull(the_column,0) < 10 THEN 1 ELSE 0 END ...
нули могут сбивать с толку. Этот. " .. ГДЕ 1 = СЛУЧАЙ ... "полезно, если вы пытаетесь передать значение Null или значение в качестве параметра ex. "Где параметр the_column=@. Этот пост может быть полезен передача Null с помощью OLEDB .
другой пример, где случай полезен, - это использование функций даты в Столбцах varchar. добавление ISDATE перед использованием say convert (colA,datetime) может не работать, и когда colA имеет данные не даты, запрос может ошибиться.