【问题标题】:How do I add a column to large sql server table如何将列添加到大型 sql server 表
【发布时间】:2017-10-20 23:41:22
【问题描述】:

我在生产中有一个包含数百万行的 SQL Server 表,结果我需要向它添加一列。或者,更准确地说,我需要在表所代表的实体中添加一个字段。

从语法上讲,这不是问题,如果表没有那么多行并且不在生产环境中,这将很容易。

我真正追求的是行动方案。有很多网站都有非常大的表格,他们必须不时添加字段。他们如何在没有大量停机时间的情况下做到这一点?

我应该补充一点,我不希望该列允许空值,这意味着我需要有一个默认值。

所以我要么需要弄清楚如何及时添加具有默认值的列,要么我需要想办法在以后更新列然后将列设置为不允许空值。

【问题讨论】:

    标签: sql sql-server tsql


    【解决方案1】:
    ALTER TABLE table1 ADD
      newcolumn int NULL
    GO
    

    不应该花那么长时间...需要很长时间的是在其他列的中间插入列... b/c 然后引擎需要创建一个新表并将数据复制到新表中。

    【讨论】:

    • 你知道吗,你是对的。只要该列的值为 NULL,它就会很快被添加。但是如果我得到一个默认值,它需要很长时间。所以我需要计划的真正问题是如何为列添加默认值。
    • 添加列,然后执行相对较小的 UPDATE 批处理以使用默认值填充列。这应该可以防止任何明显的减速。
    • 感谢 Agent_9191,这似乎是一个相当不错的方法。
    • 也许这是吹毛求疵,但我认为“b/c 然后引擎需要创建一个新表并将数据复制到新表”并不完全准确。 AFAIK,数据库引擎不允许“插入”列,只能将列添加到表定义的末尾。客户端工具负责创建临时表、复制等。
    • 当提问者明确表示他想要一个默认值时,我不明白为什么这个答案被接受了。
    【解决方案2】:

    持续正常运行时间的唯一真正解决方案是冗余

    我承认@Nestor 的回答是在 SQL Server 中添加一个新列应该不会花费很长时间,但是尽管如此,它仍然可能是生产系统上不可接受的中断。另一种方法是在并行系统中进行更改,然后在操作完成后将新的换成旧的。

    例如,如果您需要添加一个列,您可以创建一个表的副本,然后将该列添加到该副本,然后使用sp_rename() 将旧表移到一边,新表就位。

    如果您有指向此表的参照完整性约束,这会使交换变得更加棘手。在交换表时,您可能必须暂时删除约束。

    对于某些复杂的升级,您可以在单独的服务器主机上完全复制数据库。一旦准备就绪,只需交换两台服务器的 DNS 条目,瞧!

    我支持一家证券交易所公司 在 1990 年代,谁跑了三个副本 数据库服务器。那 他们可以实施升级的方式 一台服务器,同时保留一台 生产服务器和一个故障转移 服务器。他们的操作有一个 旋转的标准程序 三台机器通过生产, 故障转移和维护角色每个 天。当他们需要升级时 硬件、软件或更改 数据库架构,花了三天时间 通过他们的传播改变 服务器,但他们可以做到 服务中断。谢谢大家 冗余。

    【讨论】:

    • 您是如何在维护期间弥补错过的交易的?标准复制?
    • 证券交易所不需要 24/7 全天候运作。铃声一响,他们就关门了。
    • Doh :-) 关于如何处理 24/7 系统的想法?
    • 对;这必须通过同步数据增量来解决。复制和日志记录是常见的解决方案。这是一个相当广泛的主题。我的总体观点是高可用性和冗余是齐头并进的。
    • 嗯,这就是 SSMS 在您生成更改脚本而不是保存在工具上并等待操作完成时所做的事情。
    【解决方案3】:

    我不希望该列允许空值,这意味着我需要有一个默认值。

    从 SQL Server 2012 开始,将带有 DEFAULT 约束的 NOT NULL 列添加到任意行数(甚至数十亿行)的表变得很多容易(但仅适用于企业版)因为他们允许它是一个在线操作(在大多数情况下),对于现有的行,值将从元数据中读取,并且在更新行或重建聚集索引之前不会实际存储在行中。不再赘述,这里是 MSDN 页面中ALTER TABLE 的相关部分:

    添加 NOT NULL 列作为在线操作

    从 SQL Server 2012 Enterprise Edition 开始,当默认值为 运行时常量时,添加具有默认值的 NOT NULL 列是在线操作。这意味着无论表中的行数如何,操作几乎都是立即完成的。这是因为表中已有的行在操作过程中没有更新;相反,默认值仅存储在表的元数据中,并根据需要在访问这些行的查询中查找该值。这种行为是自动的;除了 ADD COLUMN 语法之外,不需要额外的语法来实现在线操作。运行时常量是一个表达式,它在运行时为表中的每一行生成相同的值,而不管其确定性如何。例如,常量表达式“我的临时数据”或系统函数 GETUTCDATETIME() 是运行时常量。相反,函数 NEWID() 或 NEWSEQUENTIALID() 不是运行时常量,因为为表中的每一行生成一个唯一值。添加具有不是运行时常量的默认值的 NOT NULL 列总是离线执行,并且在操作期间获取排他 (SCH-M) 锁。

    虽然现有行引用存储在元数据中的值,但对于插入的任何新行并且不为列指定其他值,默认值存储在行上。当更新行时(即使在 UPDATE 语句中未指定实际列),或者重建表或聚集索引时,存储在元数据中的默认值将移动到现有行。

    varchar(max)、nvarchar(max)、varbinary(max)、xml、text、ntext、image、hierarchyid、geometry、geography 或 CLR UDTS 类型的列不能添加到在线操作。如果这样做会导致最大可能的行大小超过 8,060 字节限制,则无法在线添加列。在这种情况下,该列作为离线操作添加。

    【讨论】:

    • 空列SQL Server 2012标准版14M行,24x7高并发怎么办?由于架构锁定,会导致明显的停机时间吗?
    • @Horaciux A NULL 列而不是 NOT NULL?如果我理解正确,那不是问题。它只是元数据,而且是即时的。在 SQL Server 2012 推出能够立即添加 NULL 列(只要它具有默认值)之前,添加列而不阻塞任何内容的唯一方法是将其添加为 NULL。但是随后您必须通过 SQL 代理作业填充它,或者每次更新执行 3000 行的集合(以避免锁升级)。所以不,您不必担心NULL 列,至少在我的经验中没有。
    【解决方案4】:

    “添加列,然后执行相对较小的 UPDATE 批处理以使用默认值填充列。这应该可以防止任何明显的减速”

    之后,您必须将该列设置为 NOT NULL,这将在一个大事务中触发。所以一切都会非常快,直到你这样做,所以你可能真的收获很少。我只从第一手经验中知道这一点。

    您可能希望将当前表从 X 重命名为 Y。您可以使用此命令 sp_RENAME '[OldTableName]' , '[NewTableName]' 来完成此操作。

    将新表重新创建为 X 并将新列设置为 NOT NULL,然后从 Y 批量插入到 X 并在新列的插入中包含默认值或在重新创建时在新列上放置默认值表十。

    我已经对具有数亿行的表进行了此类更改。仍然花了一个多小时,但它并没有炸毁我们的跨日志。当我试图用表中的所有数据将列更改为 NOT NULL 时,在我终止进程之前花了 20 多个小时。

    您是否测试过只是添加一个用数据填充它的列并将该列设置为 NOT NULL?

    所以最后我不认为有灵丹妙药。

    【讨论】:

      【解决方案5】:

      选择一个新表并重命名。示例,将第 i 列添加到表 A:

      select *, 1 as i
      into A_tmp
      from A_tbl
      
      //Add any indexes here
      
      exec sp_rename 'A_tbl', 'A_old'
      exec sp_rename 'A_tmp', 'A_tbl'
      

      应该很快,并且不会像批量插入那样触及您的事务日志。 (我今天刚刚在

      如果您需要它作为在线操作,您可以将其包装在事务中(在选择进入和重命名之间的表中可能会发生变化)。

      【讨论】:

      • 我很难理解这一点。您从A_tbl 插入A_tmp。但随后您将 A_tbl 重命名为 A_old,然后将 A_old 重命名为 A_tbl。最后的重命名不应该是从A_tmpA_tbl吗?
      • @Junto 是的,我修好了
      【解决方案6】:

      另一种技术是将列添加到新的相关表中(假设您可以通过为 FK 提供唯一索引来强制执行一对一关系)。然后,您可以批量填充它,然后您可以将连接添加到此表中您希望数据出现的任何位置。请注意,如果我不想在原始表的每个查询中使用该列,或者如果我的原始表的记录宽度变得太大,或者如果我添加了几列,我只会考虑这个。

      【讨论】:

        猜你喜欢
        • 2020-01-17
        • 1970-01-01
        • 2012-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-01
        相关资源
        最近更新 更多