Які наслідки не вказати NOT NULL у PostgreSQL для полів, які не можуть бути нульовими?


10

У мене є додаток (дані зберігаються в PostgreSQL), де більшість полів у таблицях завжди є недійсними, але схема цих таблиць цього не примушує. Наприклад, подивіться на цю підроблену таблицю:

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
Дивіться цю відповідь щодо міркувань щодо нуля та місця на диску: stackoverflow.com/questions/5008753/… Коротше кажучи, якщо у вашій таблиці більше 8 стовпців та принаймні 1 стовпчик, що зводиться на облік, для таблиці буде потрібно більше байтів у рядку, ніж якщо всі стовпці визначено не нульовим.
ypercubeᵀᴹ

1
@ ypercubeᵀᴹ: Точніше, нульова растрова карта додається лише в рядку, якщо в рядку є фактичне значення нуля: stackoverflow.com/a/7654497/939860 . Тому NOT NULLобмеження не мають прямого впливу на розмір пам’яті. Звичайно, якщо всі стовпці визначені NOT NULL, для початку не може бути нульової растрової карти. З іншого боку: розмір пам’яті, як правило, набагато менший, якщо ви використовуєте NULL замість «порожніх» або фіктивних значень для стовпців без фактичного значення, оскільки нульова растрова карта порівняно набагато менша (за винятком випадків рідкісного краю).
Ервін Брандстеттер

@ErwinBrandstetter мій поганий тоді, не зрозумів цієї частини. Отже, для стовпців, які не мають нульових значень, немає реальної різниці у сховищі, чи ви визначаєте їх як NULL чи NOT NULL, правильно? Це те ж саме і для місця для зберігання індексів?
ypercubeᵀᴹ

5
"рівень програми гарантує, що нульові значення тут не можуть відображатися" Ні, це не так. Це може переконатися, що одна програма не вставляє нулі. Але у мене є psql (наприклад), і я можу вставити нулі як навмисно, так і випадково, без того, щоб ваша програма про це знала.
Майк Шеррілл 'Відкликання котів'

5
Єдина програма, яка може переконатися, що ніхто не змінює таблицю вручну, - це самі dbms.
Майк Шеррілл 'Відкликання котів'

Відповіді:


9

Що відбувається, коли приходить новий програміст і повинен написати додаток проти цього db? Вони не знають , що поле х має бути NOT NULL.

Інша програма може припустити, що всі поля x призначені NOT NULLдля виконання підрахунків, скажімо, але деякі зараз є NULLчерез нову програму, що призводить до непослідовних і важко простежити помилки.

IMHO завжди найкраще застосовувати правила цілісності даних якомога ближче до даних, тобто в базі даних. Таким чином, нові програми та / або програмісти не можуть зіпсувати ваші дані.

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

Зробити максимально використовувати обмеження цілісності механізмів примусу вашої бази даних, навіть на шкоду продуктивності. Повільна система, яка дає правильні результати, нескінченно перевершує швидку, яка робить помилки!


1
IMHO it is always best to enforce data integrity rules as near to the data as possibleце насправді те саме, що відчуття кишечника я писав про. І саме тому я шукаю справжні виправдання. У нас є огляд коду та хороша документація, тому побоювань щодо того, щоб новий розробник не знав чогось, недостатньо для обгрунтування зміни.
Сальвадор Далі

4
Огляди коду та хороша документація не гарантують вас від (програмування чи інших) помилок.
ypercubeᵀᴹ

2
І скільки REAL PROGRAMMERSпрочитали всю (або навіть будь-яку) документацію, перш ніж застрягнути в обробці, де вони наближені?
Vérace

3
Я колись робив огляд у банку, який був таким же ставленням до їх сховища даних. У їхньому випадку - відсутність референтної цілісності. Ну, буває, 40% старих даних були сміттям, оскільки хтось не читав документацію та видаляв дані в таблицях пошуку. Ви не довіряєте оглядам коду та документації з цілісністю даних - ви робите це явним у базі даних.
TomTom

5

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

Причина полягає в тому, що оптимізатор запитів, знаючи, що стовпець не може мати NULLзначення, може виключати спеціальні тести для таких значень, як у випадку NOT INпроти NOT EXISTS. Ви можете побачити, наприклад, цей блог , де показано, що непризначення поля NOT NULL(коли таблиця містить завжди ненульові значення) за допомогою певного запиту збільшує час виконання на 500%. Результат показаний для SQL Server, але подібна поведінка може бути присутнім і в інших реляційних СУБД, як у вашій (не кажучи вже про те, що ваша база даних може бути перенесена в інші системи). Загальне правило, яке ви можете припустити, полягає в тому, що коли оптимізатору запитів буде доступно більше інформації, тоді можуть бути вироблені ефективніші плани доступу.


Дякую. Це тип відповіді, який я шукав.
Сальвадор Далі

5
Стовпці, які ніколи не містять NULL, слід визначати NOT NULLз кількох причин, жодних аргументів з цього приводу. Але посилання на блог про SQL Server не застосовується для Postgres і не підтверджує жодних згаданих вами наслідків для продуктивності. Не кажучи, що таких немає, але я хотів би побачити фактичні докази .
Ервін Брандштеттер

@ErwinBrandstetter, у мене були дуже великі сподівання щодо оптимізатора PostgreSQL :( Після декількох тестів я не знайшов суттєвих відмінностей у запиті NOT IN, представленому в блозі в PostgreSQL, без обмеження NOT NULL. Отже, я змінив відповідь , і я прошу вас, чи не вважаєте ви, що я повинен повністю його видалити.
Renzo

Ні, я не думаю, що його слід видалити. Він має 5 + голосів і жодний голос для одного.
ypercubeᵀᴹ

Семантика not inнульових стовпців різна, хоча тому в плані має бути якась різниця між двома?
Мартін Сміт

2

Космічні наслідки

Про космічні наслідки йдеться у цій публікації від @Erwin Brandstetter

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

  1. Більше 8 колонок
  2. ВСІ стовпці на столі єNOT NULL

Наслідки для продуктивності

Однак у цій публікації на SE від @Erwin Brandstetter , говорить він

  1. "Установка NOT NULL сама по собі не впливає на продуктивність. Кілька циклів для перевірки - не має значення."
  2. "... фактично використовуючи NULLs замість фіктивних значень. Залежно від типів даних, ви можете заощадити багато дискового простору та оперативної пам'яті, тим самим прискоривши .. все."

@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. Обидва методи змогли знайти null / 0 з індексом, що охоплює всю таблицю.

TLDR; Підсумок

Я не можу нічого обґрунтувати так чи інакше щодо більшості питань, які, на мою думку, варто перевірити, включаючи недоліки планувальника. Користь від використання null для збереження барана справжня. Дисковий простір, збережений за допомогою нестабільності null, є незначним, і це завищення в таблицях з одним NULLABLEстовпцем або менше 8 стовпців. У цих випадках не зберігається місця на диску.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.