我有一个生产中有数百万行的SQL Server表,事实证明我需要为其添加一列。或者,为了更准确,我需要向表所代表的实体添加一个字段。
从语法上讲,这不是问题,如果表没有那么多行而且没有生产,那么这很容易。
我真正想要的是行动方案。有很多网站都有非常大的桌子,他们必须不时添加字段。如果没有大量的停机时间,他们怎么做?
我应该添加一件事,我不希望列允许空值,这意味着我需要一个默认值。
所以我要么弄清楚如何及时添加具有默认值的列,要么我需要找出一种方法以便稍后更新列,然后将列设置为不允许空值。
答案 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提供唯一索引来强制执行一对一关系)。然后,您可以批量填充它,然后您可以将连接添加到此表中您希望数据显示的位置。注意我只考虑这个列,我不想在原始表的每个查询中使用,或者我原始表的记录宽度变得太大,或者我是否添加了多个列。