Pull to refresh

Comments 22

"...Обратимся к простому бытовому примеру: все мы задаём друг другу дежурный вопрос: «Как дела?». Часто мы получаем в ответ: «Да ничего...» Вот это «ничего» нам и нужно положить в базу данных — NULL: неопределённое, некорректное или неизвестное значение..."

В базу данных в этом случае нужно положить текст «Да ничего...». NULL мы положим, если он вообще не ответит.

А если пожмёт плечами, положим пустую строку? ))

Да, если ответ-реакция будет молчание - логично положить пустую строку. А если ответ не дойдёт- NULL.

На сколько корректно сохранять не корректное?

Нужно сохранять любой ответ - разбор - это уже следующий шаг.

Понятно, что postgresql деваться некуда, но NULL в разных СУБД, поддерживающих SQL - тяжкое наследие 70-90-х годов прошлого века. Если кто-то скажет, что NULL нужен для реляционной алгебры, то а) современные SQL-СУБД имеют минимальное отношение к реляционной алгебре, б) и в реляционной алгебре без NULL можно было обойтись. Статья хороша тем, что подчёркивает значительную часть всяких WTF, вызванных NULL.

Не пугайте меня признаками прошлого, а то ведь в ответ могу и The Third Manifesto по памяти начать цитировать! :)

Однако, «под капотом» 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(переменная или поле или выражение) может дать ноль без
дополнительных наворотов, а вот COUNT(*) - только при наличии FILTER,
без него значение гарантированно будет не меньше единицы."

- в частности. Я как-то не задумывался над этим.

"NULL - Особенное значение, обозначающее "неизвестное", "отсутствующее" или "неприменимое" значение поля или переменной"

Более того, нам неизвестно, какой из этих трёх вариантов имеет место в каком случае.

В-третьих, NULL-значения могут появляться в результате операции объединения LEFT JOIN

Мне кажется, что это некорректно. При RIGHT JOIN и FULL JOIN также возможно получение NULL-значений. Поэтому лучше переформулировать этот тезис как "NULL-значения могут появляться в результате операции внешнего соединения таблиц (кроме CROSS JOIN)"

Да, вы правы, сапасибо за уточнение. Просто изначально это было докладом на PgConf, а там тайминги, волнуешься - вот до конца корректно все детали донести не получается....

На мой взгляд будет правильнее написать: "Могут появляться в результате операции внешнего соединения (OUTER JOIN)". И не нужно упоминать про исключения.

Самое дурацкое на что напарывался в Postgres с NULL - это ситуации в запросах типа:
select * from t1 where t1.id in (select tid from t2)
Если в t2 хоть одно значение tid будет null, то весь запрос вернет пустой набор строк

Наверное, "not in"? C in проблем нет

По моему опыту, сишники имеют проблемы с пониманием 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"

Sign up to leave a comment.