如何向大型sql server表添加列

时间:2009-10-29 17:08:42

标签: sql sql-server tsql

我有一个生产中有数百万行的SQL Server表,事实证明我需要为其添加一列。或者,为了更准确,我需要向表所代表的实体添加一个字段。

从语法上讲,这不是问题,如果表没有那么多行而且没有生产,那么这很容易。

我真正想要的是行动方案。有很多网站都有非常大的桌子,他们必须不时添加字段。如果没有大量的停机时间,他们怎么做?

我应该添加一件事,我不希望列允许空值,这意味着我需要一个默认值。

所以我要么弄清楚如何及时添加具有默认值的列,要么我需要找出一种方法以便稍后更新列,然后将列设置为不允许空值。

6 个答案:

答案 0 :(得分:26)

ALTER TABLE table1 ADD
  newcolumn int NULL
GO

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

答案 1 :(得分:12)

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

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

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

如果您有指向此表的参照完整性约束,这可能使交换更加棘手。在交换表时,您可能需要简单地删除约束。

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

  

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

答案 2 :(得分:7)

  

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

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

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

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

我在拥有数亿行的表上完成了这种类型的更改。它仍然花了一个多小时,但它没有吹灭我们的trans日志。当我试图用表中的所有数据将列更改为NOT NULL时,我花了20多个小时才杀死该进程。

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

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

答案 3 :(得分:7)

  

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

从SQL Server 2012开始,将具有$(document).on("change", ".toggle-element", function() { var getParameters = $(this).data('parameters'); getParameters.top = 10; $(this).data('parameters', getParameters) alert( getParameters.top ); }); 约束的NOT NULL列添加到任意行数(甚至数十亿)的表中变得更容易很多 for Enterprise Edition)因为它们允许它是一个在线操作(在大多数情况下),对于现有行,值将从元数据中读取,而不是实际存储在行中直到更新行,或者聚簇索引是重建。而不是解释,这里是ALTER TABLE的MSDN页面中的相关部分:

  

将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字节限制,则无法在线添加列。在这种情况下,该列将添加为脱机操作。

答案 4 :(得分:3)

选择新表并重命名。示例,将列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'

应该很快,不会像批量插入那样触及您的事务日志。 (我今天刚刚在<2分钟内完成了7000万行表)。

如果您需要将其作为在线操作(在select into和renames之间的表格中可能会发生变化),您可以将其包装在事务中。

答案 5 :(得分:0)

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