SQL Server ALTER поле NOT NULL берет навсегда

Я хочу изменить поле из таблицы, содержащей около 4 миллионов записей. Я удостоверился, что все эти значения полей NOT NULL и хотят ALTER для этого поля NOT NULL

ALTER TABLE dbo.MyTable
ALTER COLUMN myColumn int NOT NULL

... Кажется, навсегда для этого обновления. Любые способы ускорить его или я застрял, просто делая это в одночасье в нерабочие часы?

Также может возникнуть блокировка таблицы?

Ответ 1

Вы можете изменить поле и сделать его не равным null без проверки полей. Если вы действительно обеспокоены тем, что вы не делаете это за несколько часов, вы можете добавить ограничение в поле, которое проверяет, чтобы убедиться, что оно не равно нулю. Это позволит вам использовать параметр "Без проверки" и не проверять каждый из 4 миллионов строк, чтобы узнать, обновляется ли он.

CREATE TABLE Test
(
    T0 INT Not NULL,
    T1 INT NUll 
)

INSERT INTO Test VALUES(1, NULL) -- Works!

ALTER TABLE Test
    WITH NOCHECK
        ADD CONSTRAINT N_null_test CHECK (T1 IS NOT NULL)

    ALTER COLUMN T1 int NOT NULL 

INSERT INTO Test VALUES(1, NULL) -- Doesn't work now!

Действительно, у вас есть два варианта (добавлено третье, см. править):

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

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

Все сказанное, если вы собираетесь выбрать вариант 2, вы можете свести к минимуму объем работы, которую вы делаете в нерабочее время. Поскольку перед изменением столбца вам необходимо обновить строки до нуля, вы можете медленно направить курсор (относительно выполнения всего этого)

  • Пройдите через каждую строку
  • Проверьте, не null ли он
  • Обновите его соответствующим образом. Это займет много времени, но он не будет блокировать весь блок таблицы, чтобы другие программы обращались к нему. (Не забудьте с (rowlock) подсказкой таблицы!)

EDIT: я просто подумал о третьем варианте: Вы можете создать новую таблицу с соответствующими столбцами, а затем экспортировать данные из исходной таблицы в новую. Когда это будет сделано, вы можете удалить исходную таблицу и изменить имя новой, чтобы она была старой. Для этого вам придется отключить зависимости от оригинала и настроить их на новый, когда вы закончите, но этот процесс значительно сократит объем работы, которую вы должны будете сделать в нерабочее время. Это тот же подход, который использует сервер sql, когда вы делаете изменения упорядочения столбцов в таблицах через студию управления. Для этого подхода я бы сделал вставку в куски, чтобы убедиться, что вы не вызываете стресс от системы и не позволяете другим получить доступ к ней. Затем в нерабочее время вы можете отказаться от оригинала, переименовать второе и применить зависимости и т.д. У вас все еще будет работать нерабочее время, но оно будет незначительным по сравнению с другим подходом.

Ссылка на использование sp_rename.

Ответ 2

Единственный способ сделать это "быстро" (*), о котором я знаю, - это

  • создание таблицы "тень", которая имеет требуемый макет
  • добавление триггера к исходной таблице, чтобы любые операции вставки/обновления/удаления копировались в теневую таблицу (ум, чтобы поймать любой NULL, который может появиться!)
  • скопируйте все данные из источника в теневую таблицу, возможно, в небольших кусках (убедитесь, что вы можете обрабатывать уже скопированные данные с помощью триггера (-ов), убедитесь, что данные будут вписываться в новую структуру (ISNULL (?)!)
  • script из всех зависимостей от/до других таблиц
  • Когда все будет сделано, выполните следующие действия в явной транзакции:
    • получить эксклюзивную блокировку таблицы в исходной таблице и одну на теневой таблице
    • запустите сценарии для отбрасывания зависимостей с исходной таблицей
    • переименуйте исходную таблицу в другое (например, суффикс _old)
    • переименуйте таблицу shadow в исходное имя исходной таблицы
    • запустите сценарии для создания всех зависимостей снова

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

Как всегда, лучше всего выполнить пробный запуск на тестовом сервере first =)

PS: не пытайтесь воссоздать FK с NOCHECK, это делает их бесполезными, поскольку оптимизатор не доверяет им и не рассматривает их при построении плана запроса.

(*: где быстро спускается: с минимальным временем простоя)

Ответ 3

Извините за разочарование, но:

  • Любые способы ускорить его: Нет, если вы хотите изменить структуру таблицы
  • или я застрял, просто делаю это в одночасье в нерабочее время? Да, и это, вероятно, к лучшему, как @HLGEM указал
  • Также может возникнуть блокировка таблицы? Да

Не имеет прямого отношения к вам (потому что он идет от NOT NULL до NULL), но интересен для чтения на эту тему: http://beyondrelational.com/blogs/sankarreddy/archive/2011/04/05/is-alter-table-alter-column-not-null-to-null-always-expensive.aspx

И, наконец, некоторая древняя история - по эквивалентному вопросу на форуме в 2005 году, такое же предложение было сделано как @Kevin, предложенное выше, - используя ограничение вместо того, чтобы сделать сам столбец нецелевым: http://www.sqlteam.com/Forums/topic.asp?TOPIC_ID=50671