改变超大表上的列大小

Tec*_*chy 6 sql-server azure-sql-database

我有一个包含 NVarChar(Max) 类型列的表。

在允许用户添加数据之前我没有采取很好的预防措施,所以现在我有超过 25 亿行,并且数据库变得超级大。

我正在尝试更改列以将大小更改为固定长度,其中应删除记录 > 该宽度。

这不是索引列。

我尝试过 LEN(Text)>width 但这是一个非常慢的函数,因为它会扫描数十亿行。

我们后来还尝试通过创建一个新表并将数据移到那里来将 Int 更改为 BigInt,但这需要一周左右的时间。

Alter Column 会冻结系统。

您能否提出任何其他方式或建议您认为最好的方法?

数据库托管在 Azure SQL 上

非常感谢 :)

Joe*_*ish 5

最好的方法总是特定于您的要求和情况。某些方法可能比其他方法更快,但需要更多临时空间或不允许最终用户查询表。根据你在问题中所说的,我将假设如下:

  • 您主要关心的是减少空间,并且您不希望必须增加数据库来执行此操作。
  • Azure SQL 限制了将数据移动到新数据库或文件组的选项。
  • 您希望该表在维护操作期间保持在线以供最终用户查询。
  • 该表具有聚集索引索引。

一种解决方法:

  1. 立即在应用程序中进行更改,以防止最终用户加载超过所需最大长度的新数据。当您弄清楚该怎么做时,没有理由让问题变得更糟。
  2. 验证所需的最大长度将释放足够的空间。如果一个简单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 数据类型的数据长度优化...了解您可能会发现性能更好的原因。

  3. 小批量从表中删除行。使用聚集键将所有行循环一次。如果您想了解更多详细信息,请尝试编写批处理脚本时小心。否则,核心思想是在处理表时增加局部变量:

    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)
  4. 完成所有删除后,停止并衡量您的进度。现在桌子够小了吗?您可能需要运行REORGANIZEREBUILD以完全回收空间。我推荐REORGANIZE它,因为它不是一个全有或全无的操作(如果你取消它,你会保持你的进度),它不会增加你的日志文件,而且它是一个在线操作。

  5. 停在这里。如果问题是表占用了太多空间,那么您已经通过从表中删除不需要的行来解决该问题。您可能不需要更改数据类型来回收空间。有一个不必要NVARCHAR(MAX)的并不理想,但缺点可能不能证明改变数据类型的努力是合理的。如果确实需要更改数据类型,那么在释放数据库中的一些空间后这样做会更容易。


KAS*_*DBA 2

我根据 APP 的需要做了类似的从 NVARCHAR(128) 更改为 NAVARCHAR(32) 的操作,使用了更改大表中的列:案例研究中描述的方法或步骤

唯一的区别是,我们按计划在几天内小批量加载数据,然后对表、索引和约束进行了所有最终更改。