使用默认值更改具有数百万行的表

时间:2016-10-30 14:23:02

标签: sql sql-server

我正在尝试运行如下所示的alter命令,除了这个表有4000万行,查询需要永远。

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

因此我考虑使用null进行更改,而不是将每列更新为4000行,并将其更改为非空。

像这样的东西

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。

1 个答案:

答案 0 :(得分: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