Что должна проверить СУБД при переводе существующего столбца из nullable в обязательный?

Что должна проверить СУБД при переводе существующего столбца из nullable в обязательный?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

СУБД должна проверить все существующие строки: в обязательном столбце не должно быть значений NULL. Если хотя бы одна такая строка найдена, изменение отклоняется; если проверка успешна, столбец получает ограничение NOT NULL, действующее и для будущих операций записи.

Исторический контекст

Реляционная модель позволяет явно описывать обязательность атрибутов отношения. Ограничение NOT NULL появилось как декларативный способ перенести правило «значение должно присутствовать» из прикладного кода в схему базы данных.

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

Постановка проблемы

Если в существующих данных уже есть NULL, простое включение NOT NULL несовместимо с текущим состоянием таблицы. СУБД не может объявить правило действующим, пока хотя бы одна строка его нарушает.

Неверное решение — сначала запретить NULL, не подготовив данные. Миграция завершится ошибкой либо, в зависимости от СУБД и способа выполнения, будет долго проверять большую таблицу и удерживать блокировки. Нельзя рассчитывать, что DEFAULT автоматически заполнит ранее сохранённые NULL.

Подробное решение

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

В конкретных СУБД синтаксис и детали блокировок различаются. Например, в PostgreSQL это можно выразить так:

ALTER TABLE orders ALTER COLUMN customer_id SET NOT NULL;

Команда завершится ошибкой, если customer_id содержит хотя бы один NULL. Она не преобразует значения, не вычисляет их и не заполняет пропуски автоматически.

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

Важен и компромисс миграции. Немедленная проверка проста и быстро делает схему строгой, но на большой таблице может быть дорогой. Поэтапная миграция снижает риск простоя, однако требует контроля новых записей, согласования с приложением и возможностей конкретной СУБД для отложенной или ускоренной проверки.

Ситуация из практики

В таблице заказов столбец идентификатора клиента исторически допускает NULL, но бизнес-правило изменилось: каждый новый заказ обязан иметь клиента. В таблице уже есть миллионы строк, а приложение работает непрерывно.

Вариант с немедленным включением NOT NULL прост, но может надолго занять ресурсы на проверку и завершиться ошибкой из-за старых пропусков. Вариант «проверять только в приложении» не даёт гарантии: другой сервис или прямой импорт всё ещё может записать NULL.

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

Что кандидаты часто упускают

  1. Вопрос: Изменит ли установка значения по умолчанию уже существующие NULL?

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

  2. Вопрос: Достаточно ли проверять обязательность столбца только в приложении?

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

  3. Вопрос: Почему исправление старых NULL нельзя бездумно выполнять перед включением ограничения?

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