我需要在数据库中的一张表格中添加一个新列。该表格包含约1.4亿行数据,我不确定如何在不锁定数据库的情况下进行操作。
由于数据库正在生产环境中使用,因此必须确保这一过程尽可能顺利。
我阅读了很多资料,但从未得到确切答案,是否会存在风险。新列可为空且默认值可以为NULL。据我所知,如果新列需要设置默认值,则问题较大。
我真的很希望能得到直接的答案。这个操作是否可行?
我需要在数据库中的一张表格中添加一个新列。该表格包含约1.4亿行数据,我不确定如何在不锁定数据库的情况下进行操作。
由于数据库正在生产环境中使用,因此必须确保这一过程尽可能顺利。
我阅读了很多资料,但从未得到确切答案,是否会存在风险。新列可为空且默认值可以为NULL。据我所知,如果新列需要设置默认值,则问题较大。
我真的很希望能得到直接的答案。这个操作是否可行?
没问题,这是完全可行的。
在一张表中添加一个允许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字节的限制,则可能仍需要长时间运行锁定。再次感谢 * 2。
第二个需要注意的地方:
测试一下。
第三个也是最后一个需要注意的地方:
真的,测试一下。
NULL_BITMAP
才会在下次更新行时扩展。 - Martin SmithUSE [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。
由于许多其他因素的影响,没有人能够确定操作需要花费多长时间。
您不应该担心操作本身,因为SQL Server正在正确执行一切:
数据库引擎在表数据定义语言(DDL)操作期间(例如添加列或删除表)使用模式修改(Sch-M)锁定。在保持此锁定的时间内,Sch-M锁定会阻止对表的并发访问。这意味着Sch-M锁定会阻塞所有外部操作,直到释放锁定。
我从未对如此多的数据执行过ALTER操作,唯一的建议是在没有太多数据库连接时进行操作(在晚上进行)。
编辑:
这里 您可以找到有关您问题的更多信息。一般来说,Matt Whitfield
是正确的。
Martin Smith
提供的链接,您会发现有一个例外。我不熟悉那里描述的情况,但据我所读,我认为最好的做法是:1.在本地机器上创建表格;2.添加一些记录;3.更改表格以添加新列;4.检查链接中的情况是否出现。 - gotqn我通常使用一种方法 - 导出该表并在本地创建新列,重新命名表名,然后导入表格,并仅将现有表格重命名并将第一个表格名称转换为原始名称。
create as select
重新创建表格更快吗?我们在 Oracle 数据库中使用这种方法。但是您需要重新创建所有约束。 - alko