清除 TEXT 列后减小表大小的注意事项

Jor*_*anT 3 index sql-server blob sql-server-2016 index-maintenance

我有第三部分应用程序,其中有一个 450GB 的表,其中在 TEXT 列中包含文件附件(是的,我知道)。通过文档保留角度,我已使用便携式驱动器将大约 35% 的旧附件导出到磁盘。TEXT 列现在有一条注释,表明附件已存档(有利于应用程序的 UI) - 从多个 MB/GB 减少到不到 1KB。

我知道反对缩小数据库的论点。我从这个论坛收集的选项包括(1)复制表、删除原始表、重命名副本、(2)使用 xp_cmdshell 'bcp...' 导出表、截断表、使用 BULK INSERT 从 'c: 导入数据。 ..' 和 ...; (3) 运行 ALTER INDEX ALL ON dbo.Table REORGANIZE WITH (LOB_COMPACTION = ON)

我最关心的是 SQL Server 将用于任何选项的资源(磁盘空间、CPU、内存...),特别是考虑到表的大小。至于时间,如果需要,我可以阻止周末时间,但是日志文件、临时数据库文件等呢?在开始使用这些选项中的任何一个之前,我应该设置/更改哪些设置?

该服务器具有 160GB 内存,其中 128GB 用于 SQL Server (2016),2 个节点,配有 20 个处理器,每个节点在 MS Windows Server 2012 R2 标准上额定频率为 2.2 GHz。

Cha*_*ace 5

您的选择的缺点似乎是:

  1. 复制和重命名表可能会弄乱存储过程和视图,需要重新编译。它还需要双倍的磁盘空间。
  2. 使用它导出bcp并重新导入它很混乱,并且您仍然需要所有这些空间,尽管不在 SQL Server 本身中。如果表很小,无论如何都比选项 1 没有任何好处。
  3. REORGANIZE WITH (LOB_COMPACTION = ON)可能会非常慢,并且可能还需要额外的空间。由于大量的页和行移动,它还会使事务日志膨胀。

比您提供的任何选项更好的选择是桌子开关

与删除/重命名的想法类似,您创建一个与原始表完全相同的新临时表。将要保留的数据插入新表中。然后截断旧的并切换新的。切换是一种快速的元数据操作,只需要交换表头。

假设您有一个现有的表

CREATE TABLE MainTable (
    id int PRIMARY KEY IDENTITY,
    document text
);
Run Code Online (Sandbox Code Playgroud)

您需要准确复制架构,这包括主/唯一/外部/检查约束和索引。IDENTITY不需要在那里,如果将其省略,可能会更容易。您可以通过右键单击 SSMS 中的表并选择 来快速创建此架构的副本Script Table As -> CREATE

CREATE TABLE CopyTable (
    id int PRIMARY KEY,
    document text
);
Run Code Online (Sandbox Code Playgroud)

然后你就可以将所有数据复制到

BEGIN TRAN;

INSERT CopyTable WITH (TABLOCKX)
  (id, document)
SELECT id, document
FROM MainTable WITH (TABLOCK);
Run Code Online (Sandbox Code Playgroud)

并截断旧表并切换

TRUNCATE TABLE MainTable;

ALTER TABLE CopyTable
  SWITCH TO MainTable;

-- if you have an IDENTITY column then:
DBCC CHECKIDENT ('MainTable');

COMMIT;
Run Code Online (Sandbox Code Playgroud)

最后删除复制表,它现在是空的:

DROP TABLE CopyTable;
Run Code Online (Sandbox Code Playgroud)

您可以移动或删除BEGIN TRAN和 ,COMMIT具体取决于同时运行的其他内容。


我建议您将列数据类型从text(已弃用)更改为varchar(max). 据我所知,它们隐式地相互转换,因此这根本不会影响应用程序。varchar(max)优点是可以在行内存储 <8kb 值,而不是在 LOB 页上。

我建议您在复制表格之前执行此操作。