31 января 2021

Быстрое заполнение нового столбца таблицы

    Недавно мне дали скрипт для обновления структуры базы данных на MS SQL Server. В нем было полно блоков, которые добавляли в таблицу столбец, заполняли его в существующих строках одинаковым значением и делали NOT NULL:
alter table MyTable add FIELD1 int null
go
update MyTable set FIELD1 = 1
go
alter table MyTable alter column FIELD1 int not null
go
Что будет, если количество строк в таблице измеряется не сотнями или тысячами, а миллионами или десятками миллионов?
    Создадим тестовую таблицу
create table MyTable 
(
  ID int identity,
  NAME varchar(50) not null,
  constraint PK_MyTable primary key (ID)
)
go
и зальем туда десять миллионов строк. На моем компьютере скрипт ALTER + UPDATE выполнялся примерно полминуты. Мне кажется, что это ужасно. Могу добавить, что в некоторые таблицы исходный скрипт добавлял несколько столбцов и делалось это отдельными блоками. То есть update изменяемой таблицы мог выполняться неоднократно.
    При создании таблицы мы можем указать столбцу значение по умолчанию, которым оно будет заполнено при добавлении новой строки, если его значение не указано. Воспользуемся значеним по умолчанию и в ALTER TABLE:
alter table MyTable 
  add FIELD1 int not null constraint DF_FIELD1 default 1
alter table MyTable drop constraint DF_FIELD1
go
Этот скрипт выполнил нужную задачу мгновенно! Дело в том, что, начиная с SQL Server 2012, существующие в таблице строки при такой операции не обновляются. Вместо обновления строк значение по умолчанию сохраняется в метаданных таблицы и используется, как значение не заполненного NOT NULL столбца. Значение по умолчанию, хранящееся в метаданных, будет перемещено в существующий столбец только при обновлении строки (даже если этот столбец не указан в UPDATE) и при перестройке таблицы или кластеризованного индекса. Для новых строк, если этот столбец не указан в INSERT, значение по умолчанию сразу сохраняется в столбце.
    Таким способом можно быстро "заполнять" значения новых столбцов не только константами, но результатом вызова функции, если он является константой времени выполнения команды. Например, для столбца типа datetime можно использовать функцию GETDATE:
alter table MyTable 
  add FIELD2 datetime not null constraint DF_FIELD2 default GETDATE()
Примером функции у которой результат не является константой времени выполнения является NEWID. Она генерирует уникальное значение GUID для каждой строки. Использующий ее скрипт
alter table MyTable 
  add FIELD3 uniqueidentifier not null constraint DF_FIELD3 default NEWID()
выполняется у меня, аналогично первоначальному варианту с ALTER + UPDATE, примерно полминуты.
    Итак, пользователям MS SQL Server 2012 и более новых версий этой СУБД повезло. У них есть способ мгновенно заполнить новые столбцы таблицы значениями, независимо от количества строк в ней.

Комментариев нет:

Отправка комментария