Документация Firebird → Документация по Firebird → NULL в СУБД Firebird → NULL в выражениях |
Как многие из нас узнали, к своему огорчению, NULL - заразен: используйте его в числовых, строковых выражениях или в выражениях, содержащих дату/время, и в результате вы всегда получите NULL; используйте его в логических (булевых) выражениях, и результат будет зависеть от типа опрерации и других вовлеченных значений.
Пожалуйста обратите внимание, что в СУБД Firebird версий до 2.0 чаще всего недопустимо прямое использование константы NULL в операциях и сравнениях. Если вы видите NULL в приведенных ниже выражениях, понимайте это как «значение поля, переменной или другого выражения, результат вычисления которого есть NULL».
Выражения в этом списке всегда возвратят NULL:
1 + 2 + 3 + NULL
'Home ' || 'sweet ' || NULL
MyField = NULL
MyField <> NULL
NULL = NULL
not (NULL)
Если вам трудно понять, почему, вспомните, что NULL - значит «неизвестно». Также давайте взглянем на следующую таблицу, где приведено объяснение каждого случая. В таблице мы не пишем NULL в выражении (как уже упоминалось, часто это недопустимо); вместо этого мы используем две сущности - A и B - которые обе являются NULL. A и B могут быть значениями полей, переменных или целых выражений со своими собственными правилами - но если только они являются NULL, они ведут себя одинаково в приведенных выражениях.
Таблица 1. Операции с NULL-сущностями A и B
Если A и B являются NULL, то: | Получаем: | Потому что: |
---|---|---|
1 + 2 + 3 + A | NULL | Если A незвестно, то 6 + A также неизвестно. |
'Home ' || 'sweet ' || A | NULL | Если A неизвестно, 'Home sweet ' || A - неизвестно. |
MyField = A | NULL | Если A неизвестно, вы не можете сказать, имеет ли MyField то же самое значение... |
MyField <> A | NULL | ...но вы так же не можете сказать, имеет ли MyField отличающееся значение! |
A = B | NULL | Если A и B незивестны, невозможно знать равны ли они. |
not (A) | NULL | Если A неизвестно, инвертированное значение также неизвестно. |
Мы уже рассмотрели, что not(NULL) дает в результате NULL. Для операторов and (логическое И) и or (логическое ИЛИ) взаимодействие несколько сложнее:
NULL or false = NULL
NULL or true = true
NULL or NULL = NULL
NULL and false = false
NULL and true = NULL
NULL and NULL = NULL
В СУБД Firebird не существует логического (булева) типа данных, тем не менее существуют константы true (истина) и false (ложь). В левой колонке таблицы с объяснениями, которую вы видите ниже, (true) и (false) представляют собой вложенные выражения, возвращающие true/false.
Таблица 2. Логические (булевые) операции с NULL-сущностью A
Если A является NULL, то: | Получаем: | Потому что: |
---|---|---|
A or (false) | NULL | «A or false» всегда имеет то же значение, что и A, которое неизвестно. |
A or (true) | true | «A or true» всегда true - содержимое A не важно. |
A or A | NULL | «A or A» всегда равно A, которое является NULL. |
A and (false) | false | «A and false» всегда false - содержимое A не важно. |
A and (true) | NULL | «A and true» всегда имеет то же значение, что и A, которое неизвестно. |
A and A | NULL | «A and A» всегда равно A, которое является NULL. |
Все эти результаты находятся в соответствии с булевой логикой. Факт состоит в том, что в порядке вычисления «X or true» и «X and false» вам просто нет необходимости знать значение X, что так же базируется на известной особенности, которую мы знаем в различных языках программирования под названием «сокращенное (ускоренное) вычисление булевых выражений».
Полученные выше результаты сокращенного вычисления могут привести вас к следующим идеям:
0 умноженное на x равно 0 для любого x. Поэтому, даже если значение x неизвестно, 0 * x равно 0. (Обратите внимание: это верно только если x имеет числовой тип данных, а не NaN или бесконечность.)
Пустая строка располагается лексикографически перед любой другой строкой. Поэтому, S >= '' равно true не зависимо от значения S.
Каждое значение равно самому себе, независимо от того, известно оно или нет. Таким образом, хотя A = B обоснованно вернет NULL, если A и B являются различными NULL-сущностями, A = A должно всегда возвращать true, даже если A является NULL.
Как это реализовано в СУБД Firebird? Что ж, мне очень жаль, но я должен сообщить вам, что, несмотря на такую неотразимую логику и аналогию с результатами булевых операций, описанных выше, следующие выражения всегда дают в итоге NULL:
0 * NULL
NULL >= ''
'' <= NULL
A = A (если A является полем или переменной со значением NULL)
Это сделано, чтобы быть последовательными.
В агрегатных функциях, таких как COUNT, SUM, AVG, MAX и MIN, NULL обрабатывается отличным образом: для вычисления результата принимаются в рассмотрение только значения полей, не содержащие NULL. То есть, если у вас есть такая таблица:
MyTable
ID | Name | Amount |
---|---|---|
1 | John | 37 |
2 | Jack | <NULL> |
3 | Joe | 5 |
4 | Josh | 12 |
5 | Jay | <NULL> |
...выражение select sum(Amount) from MyTable вернет 54, что получается из 37 + 5 + 12. Если бы все пять значений полей были просуммированы, в результате должен был бы получиться NULL. Для AVG суммируются значения полей, не содержащие NULL, а сумма делится на количество таких значений.
Есть одно исключение из этого правила: COUNT(*) вернет количество всех записей, даже тех записей, значения всех полей которых являются NULL. Но COUNT(ИмяПоля) ведет себя как и остальные агрегатные функции, то есть считает записи, в которых значение указанного поля не является NULL.
Еще одно свойство достойно упоминания. COUNT(*) и COUNT(ИмяПоля) никогда не вернут NULL: если нет записей в наборе данных, обе функции вернут 0. Также, COUNT(ИмяПоля) вернет 0, если все значения поля ИмяПоля в наборе данных являются NULL. Другие агрегатные функции вернут NULL в таком случае. Имейте в виду, что даже SUM вернет NULL, если использован пустой набор данных, что противоречит общей логике.
Документация Firebird → Документация по Firebird → NULL в СУБД Firebird → NULL в выражениях |