2016-10-30 5 views
1

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

ALTER TABLE [dbo].[ConsumerProduct] 
ADD IsPendDefault [bit] NOT NULL DEFAULT ((0)) 
GO 

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

Что-то вроде этого

ALTER TABLE [dbo].ConsumerProduct 
ADD IsPendDefault [bit] NULL 
GO 

SET ROWCOUNT 10000 

WHILE (1=1) 
BEGIN 
    BEGIN TRANSACTION 

    UPDATE ConsumerProduct 
    SET IsPendDefault = 0 
    WHERE IsPendDefault IS NULL 

    -- Update 1000 nonupdated rows 
    IF @@ROWCOUNT = 0 
    BEGIN 
     COMMIT TRANSACTION 
     BREAK 
    END 

    COMMIT TRANSACTION 
END 

ALTER TABLE [dbo].ConsumerProduct 
ALTER COLUMN IsPendDefault [bit] NOT NULL 
GO 

Даже этот запрос принимает навсегда. Есть ли более простой способ изменить таблицу со значением по умолчанию в столбце.

Я использую SQL Server 2012.

+0

Я бы использовал первичный ключ в обновлении where where и update в пакетах. Я бы сделал каждую партию автономной операцией и не полагался на цикл. Получите следующий 100 000 вариантов диапазона. –

ответ

2

В корпоративной версии SQL Server 2012

ALTER TABLE [dbo].[ConsumerProduct] 
ADD IsPendDefault [bit] NOT NULL DEFAULT ((0)) 

является an online operation. Поэтому я предполагаю, что вы не на EE.

Вам действительно нужно обновить все значения столбцов до 0?

Одним из вариантов было бы просто позволить столбец быть обнуляемым и по умолчанию NULL

ALTER TABLE [dbo].ConsumerProduct 
ADD IsPendDefault [bit] NULL 

и убедитесь, что ваш код обрабатывает NULL как false. например с SELECT ISNULL(IsPendDefault,0) AS IsPendDefault.

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

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

DECLARE @I   INT = 0, 
     @BatchSize INT = 5000; 

WHILE @I <= (SELECT MAX(ID) 
      FROM ConsumerProduct) 
    BEGIN 
     UPDATE ConsumerProduct 
     SET IsPendDefault = 0 
     WHERE IsPendDefault IS NULL 
      AND ID >= @I 
      AND ID < @I + @BatchSize; 

     SET @I = @I + @BatchSize; 
    END 

Если у вас есть большие диапазоны, которые будут пустыми или очень малонаселенной может быть стоит более сложный подход.

CREATE TABLE #processed 
    (
    ID INT PRIMARY KEY 
) 

DECLARE @ID  INT = 0, 
     @BatchSize INT = 5000; 

WHILE 1 = 1 
    BEGIN 
     WITH T 
      AS (SELECT TOP (@BatchSize) * 
       FROM ConsumerProduct 
       WHERE ID > @ID 
         AND IsPendDefault IS NULL 
       ORDER BY ID) 
     UPDATE T 
     SET IsPendDefault = 0 
     OUTPUT inserted.ID 
     INTO #processed; 

     IF @@ROWCOUNT < @BatchSize 
     BREAK; 

     SELECT @ID = MAX(ID) 
     FROM #processed; 

     TRUNCATE TABLE #processed 
    END 

DROP TABLE #processed 
+0

Вы абсолютно правы. К сожалению, сделать это null, это не вариант, потому что для этого потребуются изменения кода. У вас есть код psedo для дозирования, который вы имеете в виду. Я попробовал собственную версию пакетной обработки с использованием rowcount, но это оказалось хуже, чем решение, которое у меня там есть – prapoerfft

+0

@prapoerfft - у вас есть цельный кластеризованный первичный ключ? –

+0

Да, есть столбец идентификатора Первичный ключ в таблице, который имеет тип CLUSTERED – prapoerfft

Смежные вопросы