Поле SQL Server ALTER NOT NULL занимает вечность

Я хочу изменить поле из таблицы, в которой около 4 миллионов записей. Я убедился, что значения всех этих полей НЕ НУЛЕВЫЕ, и хочу ИЗМЕНИТЬ это поле на НЕ НУЛЕВОЕ.

ALTER TABLE dbo.MyTable
ALTER COLUMN myColumn int NOT NULL

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

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


person Chris Klepeis    schedule 20.07.2009    source источник
comment
Лично я никогда не буду вносить изменения в структуру существующей большой таблицы в любое время, кроме как в нерабочее время. Даже если это происходит довольно быстро, это может вызвать проблемы у пользователей, выполняющих какие-либо действия в момент внесения изменений. Любые серьезные изменения лучше всего делать в однопользовательском режиме. Гораздо лучше иметь затемненный плановый период обслуживания, когда пользователи ничего не могут сделать (объявленный заранее, конечно, и в не часы пик), чем иметь недовольных пользователей, которые были в процессе выполнения чего-то и получают ошибки.   -  person HLGEM    schedule 20.07.2009
comment
Участвует ли столбец, который вы меняете, в ограничениях FK?   -  person onupdatecascade    schedule 22.07.2009
comment
Из быстрого теста в профилировщике требуется Sch-M блокировка таблицы , что в принципе несовместимо. со всем. Затем он должен прочитать каждую страницу, чтобы определить, что все строки валидны.   -  person Martin Smith    schedule 18.05.2011
comment
Является ли столбец int до изменения?   -  person gbn    schedule 19.05.2011


Ответы (3)


Вы можете изменить поле и сделать его не нулевым, не проверяя поля. Если вы действительно беспокоитесь о том, чтобы не делать это в нерабочее время, вы можете добавить ограничение в поле, которое проверяет, не является ли оно нулевым. Это позволит вам использовать опцию без проверки и не проверять каждую из 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!

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

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

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

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

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

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

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

person kemiller2002    schedule 20.07.2009
comment
Если вы используете NO CHECK, ограничение не будет доверенным и не сможет использоваться оптимизатором запросов. См. sqlblog.com/blogs /tibor_karaszi/архив/12/01/2008/ - person Shannon Severance; 21.07.2009
comment
Кроме того, ключевое слово NOCHECK не применяется к NULL/NOT NULL. Он применяется только к ограничениям, которые являются частью предложения CONSTRAINT. - person Tom H; 18.05.2011
comment
Из вопроса ОП уже убедился, что их столбец не содержит значений NULL, поэтому не уверен в актуальности 2-й части вашего ответа? - person Martin Smith; 19.05.2011
comment
@martin, он, возможно, убедился в этом, но следующий человек, читающий ответ, не может. Это небольшое пространство, чтобы охватить другой сценарий, и если оператор допустил ошибку и имеет нулевые значения, то у него будет больше информации для принятия решения. - person kemiller2002; 19.05.2011

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

  • создание «теневой» таблицы с требуемым макетом
  • добавление триггера в исходную таблицу, чтобы любые операции вставки/обновления/удаления копировались в теневую таблицу (обратите внимание на любые NULL, которые могут появиться!)
  • скопируйте все данные из источника в теневую таблицу, возможно, небольшими порциями (убедитесь, что вы можете обрабатывать уже скопированные данные с помощью триггера(ов), убедитесь, что данные поместятся в новую структуру (ISNULL(?) ! )
  • скрипт из всех зависимостей от/к другим таблицам
  • when all is done, do the following inside an explicit transaction :
    • get an exclusive table lock on the source-table and one on the shadowtable
    • запустите скрипты, чтобы сбросить зависимости в исходную таблицу
    • переименуйте исходную таблицу во что-то другое (например, суффикс _old)
    • переименуйте теневую таблицу в исходное имя исходной таблицы
    • запустите скрипты, чтобы снова создать все зависимости

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

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

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

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

person deroby    schedule 23.05.2011
comment
Я обдумывал эту идею на прошлой неделе, но в нашей конкретной ситуации мы увидели проблемы, потому что может быть несколько изменений столбцов, которые не знают друг о друге, поэтому динамическое создание триггеров вместо триггеров было бы невозможно (это все быть автоматизированным). Сегодня утром мне пришло в голову, что это может быть таблица, и я собирался опубликовать ответ здесь только для того, чтобы обнаружить, что вы меня опередили :) - person Tom H; 23.05.2011

Извините за беспокойство, но:

  • Любые способы ускорить это: Нет, если вы хотите изменить саму структуру таблицы
  • или я застрял, просто делая это ночью в нерабочее время? Да, и это, вероятно, к лучшему, как указал @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

person Tao    schedule 18.05.2011