Каковы последствия отсутствия указания NOT NULL в PostgreSQL для полей, которые не могут быть нулевыми?

10

У меня есть приложение (данные хранятся в PostgreSQL), где большинство полей в таблицах всегда не равны NULL, но схема для этих таблиц не обеспечивает этого. Например, посмотрите на эту фальшивую таблицу:

CREATE TABLE "tbl" (
    "id" serial,
    "name" varchar(40),
    "num" int,
    "time" timestamp
    PRIMARY KEY ("id"),
    UNIQUE ("id")
);

Кроме того name, num, timeкоторые явно не указано , как NOT NULL, в действительности они, потому что исполнение происходит на стороне приложения.


Мне кажется, что это должно быть изменено, но в отличие от этого, уровень приложения гарантирует, что нулевые значения не могут появиться здесь, и никто больше не изменяет таблицу вручную.

Мой вопрос : каковы преимущества (производительность, хранение, согласованность, что-то еще) и недостатки (при условии, что я уже проверил, что в настоящий момент нет нулевых значений, а из бизнес-логики не должно быть нулевых значений), установив явное NOT NULLограничение?

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

Это не мое решение, поэтому именно поэтому я ищу другие оправдания. На мой взгляд, если что-то не может быть нулевым, а база данных позволяет вам указать, что что-то не является нулевым - просто сделайте это. Особенно, если изменение супер просто.

Сальвадор Дали
источник
1
Посмотрите этот ответ для значений Null и дискового пространства: stackoverflow.com/questions/5008753/… Короче говоря, если ваша таблица имеет более 8 столбцов и по крайней мере 1 столбец, допускающий значение NULL, таблице потребуется больше байтов на строку, чем если бы все столбцы были определяется не нуль.
ypercubeᵀᴹ
1
@ ypercubeᵀᴹ: если быть точным, нулевое растровое изображение добавляется к каждой строке только в том случае, если в строке есть фактическое нулевое значение: stackoverflow.com/a/7654497/939860 . Следовательно, NOT NULLограничения не оказывают прямого влияния на размер хранилища. Конечно, со всеми определенными столбцами NOT NULLне может быть нулевого растрового изображения для начала. С другой стороны: размер хранилища обычно намного меньше, если вы используете NULL вместо «пустых» или фиктивных значений для столбцов без фактического значения, потому что нулевое растровое изображение сравнительно намного меньше (за исключением случаев редких краев).
Эрвин Брандштеттер
@ErwinBrandstetter мой плохой тогда, не понимал эту часть. Таким образом, для столбцов, которые не имеют нулевых значений, нет реальной разницы в хранении, определяете ли вы их как NULL или NOT NULL, правильно? То же самое относится и к пространству хранения индекса?
ypercubeᵀᴹ
5
«уровень приложения гарантирует, что здесь не могут появляться нулевые значения» Нет, это не так. Это может гарантировать, что одно приложение не вставляет нули. Но у меня есть psql (например), и я могу вставлять нули как намеренно, так и случайно, без того, чтобы ваше приложение не знало об этом.
Майк Шеррилл 'Cat Recall'
5
Единственное приложение, которое может убедиться, что никто не изменяет таблицу вручную, - это сама dbms.
Майк Шеррилл 'Cat Recall'

Ответы:

9

Что происходит, когда прибывает новый программист, которому нужно написать приложение для этой базы данных? Они не знают , что поле х имеет быть NOT NULL.

Другая программа может предположить, что все поля x предназначены NOT NULLдля выполнения подсчета, скажем, но некоторые теперь NULLиз-за новой программы, что приводит к непоследовательности и трудно отследить ошибки.

ИМХО, всегда лучше применять правила целостности данных как можно ближе к данным, то есть в базе данных. Таким образом, новые приложения и / или программисты не смогут испортить ваши данные.

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

Сделать максимально использовать ограничение целостности механизмов принуждения вашей базы данных, даже в ущерб производительности. Медленная система, которая дает правильные результаты, бесконечно превосходит быструю, которая ошибается!

Verace
источник
1
IMHO it is always best to enforce data integrity rules as near to the data as possibleна самом деле это то же самое, что и внутреннее чувство, о котором я писал. И именно поэтому я ищу реальные оправдания. У нас есть проверка кода и хорошая документация, поэтому беспокойство о том, что новый разработчик ничего не знает, недостаточно, чтобы оправдать это изменение.
Сальвадор Дали
4
Обзоры кода и хорошая документация не гарантируют от ошибок (программирования или других).
ypercubeᵀᴹ
2
И сколько REAL PROGRAMMERSпрочитают всю (или даже любую) документацию, прежде чем застрять в проекте, где они находятся в сжатые сроки?
Verace
3
Однажды я сделал обзор в банке, который имел такое же отношение к их хранилищу данных. В их случае - нет ссылочной целостности. Что ж, бывает, что 40% старых данных были мусором, потому что кто-то не прочитал документацию и удалил данные в таблицах поиска. Вы не доверяете проверке кода и документации целостности данных - вы делаете это явным образом в базе данных.
TomTom
5

Как уже цитированный другими в комментариях, добавляя NOT NULLк вашей спецификации таблицы может улучшить в значительном образе выступления ваших запросов (в дополнении к очень хорошим методологическим причинам , изложенные в другом ответе).

Причина в том, что оптимизатор запросов, зная, что столбец не может иметь NULLзначение, может исключить специальные тесты для таких значений, как в случае « NOT INпротив» NOT EXISTS. Например, вы можете увидеть этот блог , где показано, что отсутствие объявления поля NOT NULL(когда таблица всегда содержит ненулевые значения) с определенным запросом увеличивает время выполнения на 500%. Результат показан для SQL Server, но подобное поведение может присутствовать и в других реляционных СУБД, таких как ваша (не говоря уже о том, что ваша база данных может быть перенесена в другие системы). Общее правило, которое можно принять, заключается в том, что, когда оптимизатору запросов будет доступно больше информации, могут быть созданы более эффективные планы доступа.

Renzo
источник
Спасибо. Это тип ответа, который я искал.
Сальвадор Дали
5
Столбцы, которые никогда не содержат NULL, должны быть определены NOT NULLпо нескольким причинам, без аргументов по этому поводу. Но ссылка на блог о SQL Server неприменима для Postgres и не доказывает каких-либо последствий для производительности, о которых вы упомянули. Не говорю, что их нет, но я бы хотел увидеть фактические доказательства .
Эрвин Брандштеттер
@ErwinBrandstetter, у меня было много ожиданий насчет оптимизатора PostgreSQL :( После нескольких тестов я не обнаружил существенных различий в запросе NOT IN, представленном в блоге в PostgreSQL с ограничением NOT NULL и без него. Итак, я изменил ответ и спрашиваю вас, считаете ли вы, что я должен полностью удалить его
Ренцо
Нет, я не думаю, что это должно быть удалено. У этого есть 5 + голосов и никакого отрицательного голоса, для одного.
ypercubeᵀᴹ
Семантика not inдля обнуляемых столбцов отличается, хотя в плане между ними должно быть какое-то различие?
Мартин Смит
2

Космические последствия

О космических последствиях говорит в этом посте @Erwin Brandstetter

Короче говоря, вы сохраните один totalColumns - 8бит, округленный до ближайшего байта (или MAXALIGN), если ваша база данных имеет

  1. Более 8 колонок
  2. ВСЕ столбцы в таблицеNOT NULL

Последствия для производительности

Тем не менее, в этом посте на SE от @Erwin Brandstetter он говорит

  1. «Установка NOT NULL сама по себе не влияет на производительность. Несколько циклов проверки - не имеет значения».
  2. «... фактически используя NULL вместо фиктивных значений. В зависимости от типов данных вы можете сэкономить много дискового пространства и оперативной памяти, тем самым ускоряя ... все».

У @Renzo есть ответ, в котором говорится о влиянии на производительность - я бы предположил, что ничего из этого не применимо к PostgreSQL . Я не могу найти ничего , что обосновывает любой из что , как имеющие актуальное значение для PostgreSQL. Независимо от того, какие циклы сохранены, их нельзя определить количественно даже в самом элементарном запросе.

CREATE TABLE foo (
  a int,
  b int NOT NULL,
  x float,
  y float NOT NULL
);

INSERT INTO foo ( a, b, x, y )
SELECT x, x, x, x
FROM generate_series(1,1E7) AS X(x);

EXPLAIN ANALYZE SELECT 1/a FROM foo;
EXPLAIN ANALYZE SELECT 1/b FROM foo;
EXPLAIN ANALYZE SELECT 1/x FROM foo;
EXPLAIN ANALYZE SELECT 1/y FROM foo;

Кроме того, я провел несколько тестов, чтобы увидеть, были ли NULL-индексы еще быстрее, и я не смог это доказать. Вы можете найти эту удивительно полезную ветку Скотта Марлоу в списках рассылки, в которой говорится о том, что планировщик запросов в 9.1 может использовать частичный индекс для разнородных предложений WHERE. Я проверил это, запустив следующее

CREATE TABLE foo ( a int );
CREATE TABLE bar ( a int NOT NULL );
INSERT INTO foo
  SELECT null FROM generate_series(1,1e5) AS x
  UNION ALL
  SELECT 10
  UNION ALL
  SELECT null FROM generate_series(1,1e5) AS x
;
INSERT INTO bar
  SELECT 0 FROM generate_series(1,1e5) AS x
  UNION ALL
  SELECT 10
  UNION ALL
  SELECT 0 FROM generate_series(1,1e5) AS x
;

Теперь я создал индексы,

CREATE INDEX foobar ON foo(a) WHERE a IS NOT NULL;
CREATE INDEX barbar ON bar(a) WHERE a <> 0;

В обоих этих случаях планировщик мог использовать индекс при выборе для = 10и использовал последующее сканирование при поиске NULL или 0 соответственно. Оба частичных индекса были одинакового размера. И полные индексы (не показаны) были одинакового размера. Следуя той же методологии, я загрузил таблицу с одной последовательностью 1..1e5, одним значением null / 0 и другой последовательностью 1..1e5. Оба метода смогли найти ноль / 0 с индексом, охватывающим всю таблицу.

TLDR; Резюме

Я не могу так или иначе обосновать большинство проблем производительности, которые, по моему мнению, стоили проверить, в том числе недостатки планировщика. Преимущество использования null для сохранения памяти является реальной. Дисковое пространство, сэкономленное благодаря отсутствию нулевого значения, незначительно, и это завышение для таблиц с одним NULLABLEстолбцом или менее 8 столбцов. В этих случаях не сохраняется место на диске.

Эван Кэрролл
источник