2009-10-29 36 views
19

我有一个生产中有数百万行的SQL Server表,事实证明我需要添加一列到它。或者,为了更加准确,我需要为该表所代表的实体添加一个字段。如何添加一列到大型sql服务器表

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

真的,我所追求的是行动的过程。有很多网站都有非常大的表格,他们必须不时地添加字段。他们如何在没有实质性停机的情况下做到这一点

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

因此,我需要弄清楚如何及时添加一个默认值的列,或者我需要找出一种方法来稍后更新列,然后将列设置为不允许空值。

回答

23
ALTER TABLE table1 ADD 
    newcolumn int NULL 
GO 

不应该花那么长...什么需要很长的时间来插入其他列的中间列... B/C则引擎需要创建一个新表,将数据复制到新的桌子。

+6

你知道吗,你是对的。只要该列的值为NULL,那么它的添加速度非常快。但是如果我得到一个默认值,它需要很长时间。所以我需要计划的真正问题是如何向列添加默认值。 – 2009-10-29 17:33:07

+10

添加列,然后执行相对较小的UPDATE批次以使用默认值填充列。这应该防止任何明显的减速。 – 2009-10-29 17:36:55

+0

感谢Agent_9191,这似乎是一个相当不错的方法。 – 2009-10-29 17:39:22

11

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

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

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

如果您有指向此表的参照完整性约束,这可能会使交换更加棘手。交换表格时,您可能不得不简要地删除约束条件。

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

我在1990年谁在任何时候都奔三台的重复 数据库服务器支持的交易所公司 。那 方式可以实现一台服务器上的 升级,同时保留一台 生产服务器和一台故障转移 服务器。他们的操作有一个 标准程序,通过生产 故障转移和维护角色每 日旋转三台机器。当他们需要升级 硬件,软件或更改数据库架构时, 需要花费三天的时间通过其 服务器传播更改,但它们可以在没有服务中断的情况下执行此更改。所有谢谢 冗余。

+2

在维护期间您是如何赶上错过的交易的?标准复制? – 2009-10-29 18:20:09

+0

证券交易所不需要全天候运作。他们在钟声关上。 – 2009-10-29 18:42:20

+0

Doh :-)关于如何处理24/7系统的想法? – 2009-10-29 19:12:23

7

“添加列,然后进行相对较小的UPDATE批量填充有默认值的列。这应该防止任何明显的减速”

之后,你要设置列到NOT NULL,这将在一个大交易中启动。所以一切都会运行得非常快,直到你这样做,所以你可能已经很少真正获得。我只从第一手的经验中得知这一点。

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

将新表重新创建为X,并将新列设置为NOT NULL,然后将批量插入从Y到X,并在插入新列时包含默认值,或者在您插入新列时将默认值包括在内重新创建表X.

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

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

所以最后我不认为有一个神奇的子弹。

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和rename之间的表中可能会发生变化),您可以将它包装在事务中。

+1

我很努力去理解这一点。你从'A_tbl'中插入'A_tmp'。但是,你从'A_tbl'重命名为'A_old',然后将'A_old'重命名为'A_tbl'。最后一次重命名不应该从'A_tmp'到'A_tbl'? – Junto 2016-03-10 12:41:42

+0

@Junto是的,我修复了它 – 2017-10-20 14:20:56

0

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

6

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

添加NOT NULL柱,以DEFAULT约束到任意数量的行(甚至数十亿)的表成为在SQL Server 2012中很多易启动(但仅限于企业版),因为它们允许它是一个在线操作(在大多数情况下),对于现有行,该值将从元数据中读取,而不是实际存储在行中,直到行被更新,或聚簇索引被重建。而不是意译更多,这里是从MSDN页面的相关章节ALTER TABLE

添加NOT NULL列的联机操作

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

虽然现有的行引用存储在元数据中的值,但对于插入的任何新行,缺省值存储在该行上,并且不为该列指定其他值。当更新行时(即使未在UPDATE语句中指定实际列),或者重建了表或聚集索引时,存储在元数据中的默认值将移至现有行。 类型为varchar(最大),为nvarchar(最大),VARBINARY(最大),XML,文本,NTEXT,图像,HIERARCHYID,几何形状,地理,或CLR UDTS的

列,不能在在线操作中添加。如果这样做会导致最大可能的行大小超过8,060字节限制,则无法在线添加列。在这种情况下,该列作为脱机操作添加。

+0

那么空列SQL Server 2012标准版14M行,24x7高并发性呢?由于模式锁定会导致明显的停机时间吗? – Horaciux 2017-08-31 02:55:46

+1

@Horaciux“NULL”列而不是“NOT NULL”?如果我正确地理解了这一点,那就不是问题。它仅仅是元数据,而且是相当即时的。在SQL Server 2012出现之前,只要具有默认值即可立即添加“NULL”列,添加列而不阻塞任何内容的唯一方法是将其添加为“NULL”。但是,您必须通过SQL Agent作业填充它,每个UPDATE执行3000行(避免锁升级)。所以不,你不需要担心'NULL'列,至少不是我的经验。 – 2017-08-31 16:05:28