这个 ALTER TABLE 语句需要多长时间?

xin*_*bin 3 sql-server alter-table ddl sql-server-2014

Alter TABLE [XXX] Alter column [YYY] [varchar](max) NULL
Run Code Online (Sandbox Code Playgroud)

认为

  • 有45GB的数据空间和2GB的索引空间;
  • 该表中大约有 300 万行;
  • YYY 列varchar(8000)现在可以更新(可写)。
  • 该表还有大约 30 个其他列。
  • 这台机器上有来自其他数据库和表的大约3000G数据。

一些其他信息:

  • 99.99% 的行在NULLthis 中varchar(8000)
  • Web 应用程序可能每分钟访问该表 5 次;
  • 硬件是企业级的(8 核 CPU 和 256GB RAM)。
  • 这台机器上还有其他的表和数据库,大约3000GB的数据。

相关@@VERSION详情:

Microsoft SQL Server 2014 - 12.0.4422.0 (X64)
企业版(64 位)

Aar*_*and 8

没有人可以确定或准确地告诉您此操作将在您的系统上花费多长时间。但是,我建议更改的影响可能比您想象的要小。我设置了一个快速测试来比较从int->bigint和从varchar(8000)->更改表varchar(max)。首先是两个简单的表:

CREATE TABLE dbo.t0(a int primary key, b int);
GO
CREATE TABLE dbo.t1(a int primary key, b varchar(8000));
GO
Run Code Online (Sandbox Code Playgroud)

现在,插入至少一个非 NULL 值,然后插入超过 500 万行(在我的系统上;YMMV):

INSERT dbo.t0 VALUES(0,1);
INSERT dbo.t0 SELECT rn, NULL FROM 
( SELECT rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
  FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
) AS x;
GO -- 5,299,204 rows for me

INSERT dbo.t1 VALUES(0,'what');
INSERT dbo.t1 SELECT rn, NULL FROM 
( SELECT rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
  FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
) AS x;
GO -- 5,299,204 rows for me
Run Code Online (Sandbox Code Playgroud)

然后我测试了ALTER更改,启用了 stats I/O:

SET STATISTICS IO ON;
GO
ALTER TABLE dbo.t0 ALTER COLUMN b bigint NULL; 
GO -- 9 seconds, 122,506 reads
SET STATISTICS IO OFF;
Run Code Online (Sandbox Code Playgroud)

这在 9 秒内完成,需要 122,506 次读取。

SET STATISTICS IO ON;
GO
ALTER TABLE dbo.t1 ALTER COLUMN b varchar(max) NULL; 
GO -- 5 seconds, 8,562 logical reads
SET STATISTICS IO OFF;
Run Code Online (Sandbox Code Playgroud)

这在 5 秒内完成,只需要 8,562 次读取。

因此,虽然这两个操作都没有在线进行,并且您的实际结果可能因硬件和表结构而异,但这是在 MacBook 上的低级 Windows VM 上进行的,因此我希望您的时间比这更好。

我还测试了可空列中填充的更多数据。删除表,重新创建并使用上述脚本重新填充,然后运行它以将某些内容放入 100,000 行:

;WITH x AS (SELECT TOP (100000) a,b FROM dbo.t1 ORDER BY NEWID())
UPDATE x SET b = a;
GO
;WITH x AS (SELECT TOP (100000) a,b FROM dbo.t1 ORDER BY NEWID())
UPDATE x SET b = REPLICATE(RTRIM(a), 1000);
GO
Run Code Online (Sandbox Code Playgroud)

这发生的方式长于后续ALTER秒(时间3分钟)。第一个ALTER仍然用了 9 秒,但第二个用了 17 秒,这仅仅是因为读取次数激增(达到 189,854)。这与如果您在此表中有 30 个其他列时会发生的情况类似。我仍然认为 17 秒对于这里实际发生的事情来说已经相当不错了,而且这与企业级环境相去甚远。

正如 Paul 指出的那样,从 SQL Server 2016 开始,您可以在线实现这一点,但存在限制和限制。有关更多信息,请参阅文档

ALTER TABLE dbo.t1 ALTER COLUMN b varchar(max) NULL WITH (ONLINE = ON);
Run Code Online (Sandbox Code Playgroud)

但是,此选项在 SQL Server 2014 中不可用。

您可以在网上更多地实现这一点的一种方法是将该列移动到单独的相关表中。这里有一些关于您如何切换的错综复杂的问题,它可能会影响一堆代码,但是您可以使用视图解决大多数问题(至少是暂时的)。