Комментарии 22
"...Обратимся к простому бытовому примеру: все мы задаём друг другу дежурный вопрос: «Как дела?». Часто мы получаем в ответ: «Да ничего...» Вот это «ничего» нам и нужно положить в базу данных — NULL: неопределённое, некорректное или неизвестное значение..."
В базу данных в этом случае нужно положить текст «Да ничего...». NULL мы положим, если он вообще не ответит.
Понятно, что postgresql деваться некуда, но NULL в разных СУБД, поддерживающих SQL - тяжкое наследие 70-90-х годов прошлого века. Если кто-то скажет, что NULL нужен для реляционной алгебры, то а) современные SQL-СУБД имеют минимальное отношение к реляционной алгебре, б) и в реляционной алгебре без NULL можно было обойтись. Статья хороша тем, что подчёркивает значительную часть всяких WTF, вызванных NULL.
Просто оставлю это здесь, как говорится:
Однако, «под капотом» Postgres функции и операторы считают запись, состоящую из NULL-значений, NOT NULL.
Абсолютно очевидно.
На скриншоте совершенно правильно и корректно написано "NULL - Особенное значение, обозначающее "неизвестное", "отсутствующее" или "неприменимое" значение поля или переменной" (к сожалению, в тексте сказано иначе - и совершенно зря, потому что формулировка в тексте недостаточно строгая, и даже есть определённое желание назвать её некорректной).
А Вы передаёте функциям не поле и не переменную, а запись, построенную функцией ROW constructor. И в отличие от NULL, эта запись является вполне себе реально существующим объектом, известным и присутствующим.
Почти все бинарные операции с NULL — сложить, вычесть, умножить, конкатенировать — дают на выходе NULL.
...
Тем не менее, логическая операция TRUE OR NULL на выходе даёт TRUE. FALSE AND NULL даёт в результате FALSE. То есть существуют некоторые исключения из общего правила.
Всё это весьма логично, если в качестве объяснения-замены NULL использовать "неизвестное значение". Например, и TRUE OR TRUE = TRUE, и TRUE OR FALSE = TRUE, следовательно TRUE OR "неизвестное значение" всегда, каким бы это неизвестное значение не было, даёт TRUE, других вариантов просто нет. Тогда как "что-то" + "неизвестное значение" вполне себе очевидно даёт "неизвестное значение". Так что тут скорее формулировка подкачала - первая из процитированных фраз явно не тянет на "общее правило".
Аналогичные рассуждения можно применить и к сравнению.
а вот Count со звёздочкой посчитает всё, включая NULL-значения.
Об этом я уже говорил выше - COUNT(*) обрабатывает/считает не значения, а записи. Кстати, именно поэтому COUNT(переменная или поле или выражение) может дать ноль без дополнительных наворотов, а вот COUNT(*) - только при наличии FILTER, без него значение гарантированно будет не меньше единицы.
может дать ноль … COUNT(*) — только при наличии FILTER, без него значение гарантированно будет не меньше единицы
Или если в таблице, по которой делается count
, вообще нет ни одной строки.
Да, спасибо за отзыв вообще и вот за это замечание:
"COUNT(переменная или поле или выражение) может дать ноль без
дополнительных наворотов, а вот COUNT(*) - только при наличии FILTER,
без него значение гарантированно будет не меньше единицы."
- в частности. Я как-то не задумывался над этим.
"NULL - Особенное значение, обозначающее "неизвестное", "отсутствующее" или "неприменимое" значение поля или переменной"
Более того, нам неизвестно, какой из этих трёх вариантов имеет место в каком случае.
В-третьих, NULL-значения могут появляться в результате операции объединения LEFT JOIN
Мне кажется, что это некорректно. При RIGHT JOIN и FULL JOIN также возможно получение NULL-значений. Поэтому лучше переформулировать этот тезис как "NULL-значения могут появляться в результате операции внешнего соединения таблиц (кроме CROSS JOIN)"
Да, вы правы, сапасибо за уточнение. Просто изначально это было докладом на PgConf, а там тайминги, волнуешься - вот до конца корректно все детали донести не получается....
На мой взгляд будет правильнее написать: "Могут появляться в результате операции внешнего соединения (OUTER JOIN)". И не нужно упоминать про исключения.
По моему опыту, сишники имеют проблемы с пониманием NULL, потому что в этих их (теперь и моих тоже) сях настоящего NULL нету. Я заходил со стороны Javascript, поэтому был морально готов, но некоторые нюансы поведения в базе данных до сих пор иногда упускаю по невнимательности. Особенно при агрегации.
Небольшая шпаргалка по особенностям сравнения record и NULL
Testing a ROW expression with IS NULL only reports TRUE if every single column is NULL. Нужно об этом знать, чтобы на напороться на ошибки в своём коде.
SELECT
(NULL, NULL) IS NULL as "(NULL, NULL) IS NULL", --true
(NULL, NULL) IS NOT NULL as "(NULL, NULL) IS NOT NULL", --false
NOT (NULL, NULL) IS NULL as "NOT (NULL, NULL) IS NULL", --false
(1, NULL) IS NULL as "(1, NULL) IS NULL", --false
(1, NULL) IS NOT NULL as "(1, NULL) IS NOT NULL", --false --!!!
NOT (1, NULL) IS NULL as "NOT (1, NULL) IS NULL" --true --!!!
Я бы рекомендовал интересующимся вначале (перед этой статьей) почитать Дейта "Проектирование баз данных", на тему null, и особенно на тему null в Foreign Key.
Натыкался на то, что уникальный индекс в Oracle игнорирует строки с NULL. А в PostgreSQL добавляет их, и не позволяет добавлять несколько NULL в таблицу. Приходится делать частичный индекс с условием "WHERE поле IS NOT NULL"
NULL-значения в PostgreSQL: правила и исключения