向新数据库表添加新列

时间:2013-11-12 09:12:42

标签: sql sql-server database

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

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

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

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

4 个答案:

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

警告位:

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

编辑 - > @MartinSmith指出NULL位图只会在行改变时展开,非常感谢。但是,正如他也指出的那样,如果行的大小超过SQL Server 2012中的8060字节限制,那么a long running lock may still be required。非常感谢* 2。

第二个警告:

测试它。

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

不,真的,测试一下。

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

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

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

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

答案 2 :(得分:5)

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

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

  

数据库引擎在使用期间使用架构修改(Sch-M)锁定   表数据定义语言(DDL)操作,如添加一个   列或删除表。在它举行期间,Sch-M   锁防止并发访问表。这意味着Sch-M   锁定将阻止所有外部操作,直到锁定被释放。

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

编辑:

Here您可以找到有关您问题的更多信息。通常,Matt Whitfield是正确的

  

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

以及何时

  

新列可以为空,默认为NULL。表格的元数据   记录新列存在但可能不存在的事实   记录。这就是为什么空位图也具有数量的计数   该特定记录中的列。 SQL Server可以解决一个问题   列是否存在于记录中。所以 - 这不是一个   数据大小操作 - 不更新现有表记录   何时添加新列。记录将仅在更新时更新   它们会针对其他一些操作进行更新。

答案 3 :(得分:1)

我通常有一种方法 - 导出该表并在本地创建新列并重命名表名,然后导入表表,只需重命名现有表并将第一个表名转换为wa original。