Быстро изменить столбец NULL на NOT NULL

11

У меня есть таблица с миллионами строк и столбцом, который допускает значения NULL. Однако ни одна строка в настоящее время не имеет значения NULL для этого столбца (я могу достаточно быстро это проверить с помощью запроса). Однако, когда я выполняю команду

ALTER TABLE MyTable ALTER COLUMN MyColumn BIGINT NOT NULL;

запрос длится вечно условно говоря. На самом деле это занимает от 10 до 20 минут, более чем вдвое дольше, чем добавление проверочного ограничения. Есть ли способ мгновенно обновить метаданные таблицы для этого столбца, тем более что я знаю, что ни одна строка не имеет значения NULL для этого столбца?

Джозеф Дейгл
источник
2
Нет (или, по крайней мере, не используя документированные / поддерживаемые методы). См. Почему ALTER COLUMN в NOT NULL вызывает значительный рост файла журнала?
Мартин Смит
2
Возможно, он также ждал Sch-Mблокировки, когда он «навсегда». Вы смотрели, чтобы увидеть, ожидал ли он или занят?
Мартин Смит
@MartinSmith Я разъяснил, что я имею в виду под навсегда . Я тестирую это в среде разработчиков, и никакие другие сессии не попадают в базу данных. Это в конечном итоге завершено. Но ответ, который вы связали, объясняет, почему это занимает так много времени. Если вы можете перефразировать ваш комментарий как ответ, тогда я приму его.
Джозеф Дейгл

Ответы:

12

Ответ @ ypercube справляется с этим частично только из-за изменения метаданных.

Добавление ограничения NOCHECKозначает, что для его проверки не нужно читать строки, и если вы начинаете с позиции, в которой столбец не содержит NULLзначений (и если вы знаете, что между проверкой и добавлением ограничения не будет добавлено ни одной), поскольку ограничение предотвращает NULLсоздание значений из будущего INSERTили UPDATEопераций, это будет работать.

Однако добавление ограничения может повлиять на одновременные транзакции. ALTER TABLEНужно будет приобрести Sch-Mзамок первым. Пока он ожидает этого, все другие обращения к таблице будут заблокированы, как описано здесь .

Однако после того, как Sch-Mблокировка получена, операция должна быть довольно быстрой.

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

CREATE TABLE T (X INT NULL)

INSERT INTO T 
SELECT ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values

ALTER TABLE T WITH NOCHECK
  ADD  CONSTRAINT X_NOT_NULL 
    CHECK (X IS NOT NULL) ; 

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

Строить планы

Сравните это с более простым

ALTER TABLE T ALTER COLUMN X INT NOT NULL

SELECT *
FROM T 
WHERE X NOT IN (SELECT X FROM T)

Строить планы

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

Возможно, на полпути можно добавить ограничение проверки WITH CHECK. Это будет медленнее, чем WITH NOCHECKнужно для чтения всех строк, но это позволяет оптимизатору запросов дать более простой план в запросе выше, и это должно избежать возможной проблемы с записанными обновлениями.

Мартин Смит
источник
7

Вместо изменения столбца вы можете добавить CHECKограничение таблицы с NOCHECKпараметром:

ALTER TABLE MyTable WITH NOCHECK
  ADD  CONSTRAINT MyColumn_NOT_NULL 
    CHECK (MyColumn IS NOT NULL) ;
ypercubeᵀᴹ
источник
1
Это предотвратит будущие обновления или вставки, которые составляют столбец, NULLно не смогут использоваться оптимизатором запросов.
Мартин Смит
@MartinSmith О, да, я только что прочитал ответ и комментарии к аналогичному вопросу: как добавить столбец NOT NULL в большую таблицу в SQL Server? Пожалуйста, добавьте ответ с проблемами или лучшее решение, и я удалю мой.
ypercubeᵀᴹ
2
У меня нет лучшего решения. Я проголосовал за это, потому что это обеспечивает частичное решение. Если все, что ОП хочет сделать, - это предотвратить недействительные данные, то это сработает (и должно быть быстрее, чем ALTER COLUMNпосле получения Sch-Mблокировки, это вообще не требует сканирования строк). Просто отметив, что это не совсем то же самое (например, если использовать в NOT INзапросе, план будет более сложным)
Martin Smith