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 не имеет/не имел первоклассных логических значений, или, по крайней мере, я думал, что это не так.)

Итак, мои вопросы здесь:

  1. это действительно верно для SQL Server (или, возможно, back-rev SQL Server 2000 или 2005) или я просто спятил?
  2. если так же нюанс применить к PostgreSQL? (8.4 если это важно)
  3. в чем именно проблема? Это связано с тем, как работают индексы или что-то еще?

мое заземление в 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 имеет данные не даты, запрос может ошибиться.