Скопируйте один столбец в другой для более миллиарда строк в базе данных SQL Server - PullRequest
9 голосов
/ 22 сентября 2010

База данных: SQL Server 2005

Проблема: скопировать значения из одного столбца в другой столбец в той же таблице с миллиардом + строки.

test_table (int id, bigint bigid)

Вещи пробовали 1: запрос на обновление

update test_table set bigid = id 

заполняет журнал транзакций и выполняет откат из-за недостатка места в журнале транзакций.

Пробовал 2 - процедура в следующих строках

set nocount on
set rowcount = 500000
while @rowcount > 0
begin
 update test_table set bigid = id where bigid is null
 set @rowcount = @@rowcount
 set @rowupdated = @rowsupdated + @rowcount
end
print @rowsupdated

Вышеописанная процедура начинает замедляться по мере продолжения.

Пробовал 3 - Создание курсора для обновления.

как правило, не рекомендуется в документации по SQL Server, и этот подход обновляет по одной строке за раз, что занимает слишком много времени.

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

Любые подсказки, указатели будут высоко оценены.

Ответы [ 7 ]

5 голосов
/ 22 сентября 2010

Используйте TOP в операторе UPDATE :

UPDATE TOP (@row_limit) dbo.test_table
   SET bigid = id 
 WHERE bigid IS NULL
4 голосов
/ 22 сентября 2010

Я собираюсь предположить, что вы приближаетесь к пределу в 2,1 миллиарда для типа данных INT для искусственного ключа для столбца. Да, это боль. Гораздо проще исправить до факта, чем после того, как вы действительно достигнете этого предела, и производство будет остановлено, пока вы пытаетесь это исправить:)

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

Рост журнала

Изначально журнал взорвался, потому что он пытался зафиксировать все строки 2b одновременно. Подсказки в других постах для "разбивки на части" будут работать, , но , которые могут не полностью решить проблему журнала.

Если база данных находится в режиме SIMPLE, все будет в порядке (журнал будет повторно использоваться после каждой партии). Если база данных находится в режиме восстановления FULL или BULK_LOGGED, вам придется часто запускать резервные копии журналов во время выполнения вашей операции, чтобы SQL мог повторно использовать пространство журнала. Это может означать увеличение частоты резервного копирования в течение этого времени или просто мониторинг использования журнала во время работы.

Индексы и скорость

ВСЕ из ответов where bigid is null будут замедляться при заполнении таблицы, поскольку в новом поле BIGID (предположительно) нет индекса. Вы могли бы (конечно) просто добавить индекс на BIGID, но я не уверен, что это правильный ответ.

Ключ (предназначен для каламбура) - это мое предположение, что исходное поле идентификатора, вероятно, является первичным ключом, или кластеризованным индексом, или обоими. В этом случае, давайте воспользуемся этим фактом и сделаем вариацию идеи Джесса:

set @counter = 1
while @counter < 2000000000 --or whatever
begin
  update test_table set bigid = id 
  where id between @counter and (@counter + 499999) --BETWEEN is inclusive
  set @counter = @counter + 500000
end

Это должно быть очень быстро из-за существующих индексов по ID.

Проверка ISNULL на самом деле не нужна, равно как и мой (-1) на интервале. Если мы дублируем несколько строк между вызовами, это не имеет большого значения.

2 голосов
/ 22 сентября 2010

Это разовая вещь?Если это так, просто сделайте это по диапазонам:

set counter = 500000
while @counter < 2000000000 --or whatever your max id
begin
 update test_table set bigid = id where id between (@counter - 500000) and @counter and bigid is null
 set counter = @counter + 500000
end
2 голосов
/ 22 сентября 2010

Вы можете попробовать использовать что-то вроде SET ROWCOUNT и выполнить пакетное обновление:

SET ROWCOUNT 5000;

UPDATE dbo.test_table 
SET bigid = id 
WHERE bigid IS NULL
GO

, а затем повторите это столько раз, сколько вам нужно.

Таким образом, вы избегаете симптомов RBAR (row-by-agonizing-row) у курсоров и циклов while, и все же вы не заполняете журнал транзакций без необходимости.

Конечно, между прогонами вам придется делать резервные копии (особенно вашего журнала), чтобы сохранить его размер в разумных пределах.

0 голосов
/ 22 сентября 2010

Я второй оператор UPDATE TOP (X)

Также, чтобы предложить, если вы находитесь в цикле, добавьте некоторую задержку WAITFOR или COMMIT между ними, чтобы позволить другим процессам некоторое время использовать таблицу, еслинеобходимо против блокировки навсегда, пока все обновления не будут завершены

0 голосов
/ 22 сентября 2010

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

Другой вариант, немного выходящий за рамки размышлений ... Вы можете выразить обновление таким образом, чтобы вы могли материализовать значения столбцов в выборке??Если вы можете сделать это, вы можете создать то, что составляет НОВУЮ таблицу, используя SELECT INTO, которая является минимально зарегистрированной операцией (при условии, что в 2005 году вы установили модель восстановления SIMPLE или BULK LOGGED).Это будет довольно быстро, и тогда вы можете удалить старую таблицу, переименовать ее в старое имя таблицы и воссоздать любые индексы.

select id, CAST(id as bigint) bigid into test_table_temp from test_table
drop table test_table
exec sp_rename 'test_table_temp', 'test_table'
0 голосов
/ 22 сентября 2010

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

set rowcount 500000
update test_table tt1
set bigid = (SELECT tt2.id FROM test_table tt2 WHERE tt1.id = tt2.id)
where bigid IS NULL

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

ALTER DATABASE db1
SET RECOVERY SIMPLE
GO

update test_table
set bigid = id
GO

ALTER DATABASE db1
SET RECOVERY FULL
GO
...