向新数据库表添加新列

FRE*_*RIK 16 sql database sql-server

我需要在我的数据库中的表中添加一个新列.该表包含大约1.4亿行,我不知道如何在不锁定数据库的情况下继续操作.

数据库正在生产中,这就是为什么它必须尽可能顺利.

我已经阅读了很多,但如果这是一个冒险的操作,从来没有真正得到答案.新列可以为空,默认值可以为NULL.据我所知,如果新列需要默认值,则存在更大的问题.

我真的很感激这个问题的一些直截了当的答案.这是否可行?

Mat*_*eld 9

是的,这显然是可行的.

添加NULL可接受并且没有默认值的列不需要长时间运行的锁来向表中添加数据.

如果提供默认值,则SQL Server必须更新每条记录,以便将新列值写入该行.

它的工作原理如下:

+---------------------+------------------------+-----------------------+
| Column is Nullable? | Default Value Supplied | Result                |
+---------------------+------------------------+-----------------------+
| Yes                 | No                     | Quick Add (caveat)    |
| Yes                 | Yes                    | Long running lock     |
| No                  | No                     | Error                 |
| No                  | Yes                    | Long running lock     |
+---------------------+------------------------+-----------------------+
Run Code Online (Sandbox Code Playgroud)

警告位:

我不记得当我添加一个导致NULL位图大小扩展的列时会发生什么.我想说NULL位图代表当前行中所有列的可空性,但是我不能把我的手放在心上并说这绝对是真的.

编辑 - > @MartinSmith指出NULL位图只会在行改变时展开,非常感谢.但是,正如他也指出的那样,如果行的大小超出SQL Server 2012中的8060字节限制,则可能仍然需要长时间运行的锁定.非常感谢*2.

第二个警告:

测试一下.

第三个也是最后一个警告:

不,真的,测试一下.


Ale*_*exK 6

我的示例是如何将新列添加到表中数千万行,并按默认值填充它而不会长时间运行锁定

USE [MyDB]
GO

ALTER TABLE [dbo].[Customer] ADD [CustomerTypeId] TINYINT NULL
GO
ALTER TABLE [dbo].[Customer] ADD CONSTRAINT [DF_Customer_CustomerTypeId] DEFAULT 1 FOR [CustomerTypeId]
GO
DECLARE @batchSize bigint = 5000
    ,@rowcount int
    ,@MaxID int;

SET @rowcount = 1
SET @MaxID = 0

WHILE @rowcount > 0
BEGIN
    ;WITH upd as (
        SELECT TOP (@batchSize)
            [ID]
            ,[CustomerTypeId]
        FROM [dbo].[Customer] (NOLOCK)
        WHERE [CustomerTypeId] IS NULL
            AND [ID] > @MaxID
        ORDER BY [ID])

    UPDATE upd
          SET [CustomerTypeId] = 1
              ,@MaxID = CASE WHEN [ID] > @MaxID THEN [ID] ELSE @MaxID END

    SET @rowcount = @@ROWCOUNT
    WAITFOR DELAY '00:00:01'
END;

ALTER TABLE [dbo].[Customer]  ALTER COLUMN [CustomerTypeId] TINYINT NOT NULL;
GO
Run Code Online (Sandbox Code Playgroud)

ALTER TABLE [dbo].[Customer] ADD [CustomerTypeId] TINYINT NULL 仅更改元数据(Sch-M锁)和锁定时间不依赖于表中的行数

之后,我按小部分(5000行)按默认值填充新列.我在每个周期后等一秒钟,以免过于激进地阻止桌子.我有一个int列"ID"作为主群集键

最后,当所有新列都填满后,我将其更改为NOT NULL

  • 您应该描述您正在尝试解决的问题。(我可以通过阅读代码来推断它,但对于普通读者来说并不是显而易见的。) (2认同)

got*_*tqn 5

没有人能够知道运营成本会花多少时间,因为这毕竟取决于许多其他因素.

您不应该担心操作本身,因为SQL Server正在做正确的事情:

数据库引擎在表数据定义语言(DDL)操作期间使用模式修改(Sch-M)锁定,例如添加列或删除表.在保持期间,Sch-M锁防止并发访问该表.这意味着Sch-M锁会阻止所有外部操作,直到释放锁定为止.

我从来没有ALTER对这么多的数据进行过操作,我唯一能给出的建议就是在没有那么多数据库连接的情况下(夜间).

编辑:

您可以在此处找到有关您问题的更多信息.一般来说,Matt Whitfield是对的

向表中添加列的唯一时间是导致数据大小操作(即修改表中每一行的操作)是新列具有非null默认值.

什么时候

新列可以为空,默认为NULL.表的元数据记录了新列存在但可能不在记录中的事实.这就是为什么空位图还具有该特定记录中列数的原因.SQL Server可以确定记录中是否存在列.所以 - 这不是数据大小操作 - 添加新列时不会更新现有的表记录.只有在更新某些其他操作时,才会更新记录.