Tec*_*chy 6 sql-server azure-sql-database
我有一个包含 NVarChar(Max) 类型列的表。
在允许用户添加数据之前我没有采取很好的预防措施,所以现在我有超过 25 亿行,并且数据库变得超级大。
我正在尝试更改列以将大小更改为固定长度,其中应删除记录 > 该宽度。
这不是索引列。
我尝试过 LEN(Text)>width 但这是一个非常慢的函数,因为它会扫描数十亿行。
我们后来还尝试通过创建一个新表并将数据移到那里来将 Int 更改为 BigInt,但这需要一周左右的时间。
Alter Column 会冻结系统。
您能否提出任何其他方式或建议您认为最好的方法?
数据库托管在 Azure SQL 上
非常感谢 :)
最好的方法总是特定于您的要求和情况。某些方法可能比其他方法更快,但需要更多临时空间或不允许最终用户查询表。根据你在问题中所说的,我将假设如下:
一种解决方法:
验证所需的最大长度将释放足够的空间。如果一个简单COUNT_BIG(*)
的太慢,你可以使用TABLESAMPLE
一些数学来计算你将删除多少行。我在我的本地机器上创建了一个 100 GB 的表,并且运行一个 1% 的示例查询用了不到一秒钟的时间使用冷缓存。示例代码如下:
DROP TABLE IF EXISTS #BIG_TABLE;
CREATE TABLE #BIG_TABLE (
ID BIGINT NOT NULL,
FOR_U NVARCHAR(MAX),
PRIMARY KEY (ID)
);
DECLARE @big_string NVARCHAR(MAX) = REPLICATE(CAST(N'Z' AS VARCHAR(MAX)), 200000);
INSERT INTO #BIG_TABLE WITH (TABLOCK)
SELECT TOP (250000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN, @big_string -- 100k is good
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);
CHECKPOINT;
DBCC DROPCLEANBUFFERS;
SELECT COUNT_BIG(*) total_sampled_rows
, COUNT_BIG(CASE WHEN DATALENGTH(FOR_U) > 8000 THEN 1 ELSE NULL END) sampled_rows_too_long
FROM #BIG_TABLE TABLESAMPLE (1 PERCENT);
Run Code Online (Sandbox Code Playgroud)
注意使用DATALENGTH
代替LEN
。请参阅LOB 数据类型的数据长度优化...了解您可能会发现性能更好的原因。
小批量从表中删除行。使用聚集键将所有行循环一次。如果您想了解更多详细信息,请尝试编写批处理脚本时小心。否则,核心思想是在处理表时增加局部变量:
DECLARE @start_id BIGINT = 1;
DECLARE @end_id BIGINT = 500;
DELETE FROM #BIG_TABLE
WHERE ID BETWEEN @start_id AND @end_id;
Run Code Online (Sandbox Code Playgroud)完成所有删除后,停止并衡量您的进度。现在桌子够小了吗?您可能需要运行REORGANIZE
或REBUILD
以完全回收空间。我推荐REORGANIZE
它,因为它不是一个全有或全无的操作(如果你取消它,你会保持你的进度),它不会增加你的日志文件,而且它是一个在线操作。
停在这里。如果问题是表占用了太多空间,那么您已经通过从表中删除不需要的行来解决该问题。您可能不需要更改数据类型来回收空间。有一个不必要NVARCHAR(MAX)
的并不理想,但缺点可能不能证明改变数据类型的努力是合理的。如果确实需要更改数据类型,那么在释放数据库中的一些空间后这样做会更容易。
我根据 APP 的需要做了类似的从 NVARCHAR(128) 更改为 NAVARCHAR(32) 的操作,使用了更改大表中的列:案例研究中描述的方法或步骤
唯一的区别是,我们按计划在几天内小批量加载数据,然后对表、索引和约束进行了所有最终更改。