我可以批量插入空的页面压缩表并获得完全压缩吗?

Cai*_*haw 7 sql-server bcp compression sql-server-2016

我有很多大表(大约 1000 万行宽)需要定期加载到 SQL Server 2016 中以进行只读报告。我希望这些表在磁盘上尽可能小,这比加载或查询的性能改进更重要。

这是我对不需要进一步索引的表所做的工作:

  1. 使用DATA_COMPRESSION=PAGE.
  2. 使用 bcp 将平面文件中的数据批量插入到新表中。

表中的列类型是 varchar(不超过 512,不是最大值)、float、tinyint 或日期(不是日期时间)。所有列都创建为可为空的,并且没有定义主键或外键——它们与查询无关,表永远不会直接更新。一切的默认排序规则是SQL_Latin1_General_CP1_CI_AS.

当我这样做时,我可以看到sys.allocation_units该页面数据压缩已应用于堆,并且我可以看到sys.partitions填充因子正确为 0 (100%)。由于表比未压缩的表小得多,我认为压缩已完成。

但是,如果我然后使用相同的选项重建DATA_COMPRESSION=PAGE,则假定已经压缩的表会缩小大约 30%!看起来它从每个数据页大约 17 行到每页 25 行。(虽然只有一次。在那之后再次重建不会使它比第一次重建更小。)

问题

所以我的问题是:(a)这里发生了什么?(b) 有没有办法在加载表时直接获得这个超小的压缩大小,而无需在加载数据后重建?

Ran*_*gen 8

@HandyD是完全正确的,我只想强调一些其他方法来在插入堆时进行压缩。

来自同一个文件

当堆配置为页面级压缩时,页面仅通过以下方式接收页面级压缩:

  • 在启用批量优化的情况下批量导入数据。
  • 数据是使用 INSERT INTO ... WITH (TABLOCK) 语法插入的,并且表没有非聚集索引。
  • 通过执行带有 PAGE 压缩选项的 ALTER TABLE ... REBUILD 语句重建表。

据此,您可以利用最少记录的批量插入或使用INSERT INTO ... WITH (TABLOCK) 来获得PAGE压缩,而无需进行重建。


(a) 这里发生了什么?(b) 有没有办法在加载表时直接获得这个超小的压缩大小,而无需在加载数据后重建?

PAGE插入堆时有获得压缩的规则,添加-h "TABLOCK"到您的bcp命令中以获得压缩。

ROW压缩无需这些先决条件即可工作,并且是以下示例中使用的最少压缩量,感谢@DenisRubashkin指出这一点!


测试

示例启动数据和 BCP 输出命令


--Tested on SQL Server 2014 SP2

CREATE TABLE dbo.CompressedHeap_Source( Val varchar(512), 
                                 Datefield Date, 
                                 Tinyfield TinyINT,
                                 Floatfield float) 
WITH (DATA_COMPRESSION = PAGE);

INSERT INTO dbo.CompressedHeap_Source
(
Val,Datefield,Tinyfield,Floatfield)

SELECT 'Bla',cast(getdate() as date),1,1.2412
FROM master..spt_values spt1
CROSS APPLY master..spt_values spt2;

--bcp TEST.dbo.CompressedHeap_Source out E:\Data\HeapData.bcp -c -T
Run Code Online (Sandbox Code Playgroud)

ROW压缩和非压缩尺寸

132272 KB对堆进行标准插入时的数据大小为 ROW压缩但未PAGE压缩。

176216 KB对于我们的测试,没有任何压缩的数据大小是〜。

exec sp_spaceused 'dbo.CompressedHeap_Source'

name                    rows                    reserved    data      index_size    unused
CompressedHeap_Source   6365530                 132296 KB   132272 KB   8 KB    16 KB
Run Code Online (Sandbox Code Playgroud)

插入 ... 使用 TABLOCK

插入WITH TABLOCK为我们提供了PAGE压缩数据大小,69480 KB.

INSERT INTO dbo.CompressedHeap_Source2  WITH(TABLOCK)
(
Val,Datefield,Tinyfield,Floatfield)

SELECT 'Bla',cast(getdate() as date),1,1.2412
FROM master..spt_values spt1
CROSS APPLY master..spt_values spt2
Run Code Online (Sandbox Code Playgroud)

批量插入

现在,当我们创建一个也被page压缩的目标堆表并执行批量插入时with tablock

CREATE TABLE dbo.CompressedHeap_Destination( Val varchar(512), 
                                 Datefield Date, 
                                 Tinyfield TinyINT,
                                 Floatfield float) 
WITH (DATA_COMPRESSION = PAGE);

bulk insert dbo.CompressedHeap_Destination

from 'E:\Data\HeapData.bcp'  with (TABLOCK)
Run Code Online (Sandbox Code Playgroud)

数据被page压缩并且也在69480 KB

name    rows    reserved    data    index_size  unused
CompressedHeap_Destination  6365530                 69512 KB    69480 KB    8 KB    24 KB
Run Code Online (Sandbox Code Playgroud)

BCP 输入与 TABLOCK

与提示一起BULK INSERT WITH TABLOCK使用可以获得与 相同的结果。这是有道理的,他们在内部做同样的事情BCP IN-h "TABLOCK"

--bcp TEST.dbo.CompressedHeap_Destination2 IN E:\Data\HeapData.bcp -c -T -h "TABLOCK"
Run Code Online (Sandbox Code Playgroud)

结果大小为 69480 KB

BCP 输入,无 TABLOCK

使用 BCP 从目标表的副本中的同一文件加载数据

标准 bcp 命令会生成非压缩数据:

--bcp TEST.dbo.CompressedHeap_Destination2 IN E:\Data\HeapData.bcp -c -T 
Run Code Online (Sandbox Code Playgroud)

数据大小为132272 KB(行压缩)。

  • 我认为重建“压缩”表会减小它的大小。似乎该表是 ROW 压缩的,必须重建才能获得 PAGE 压缩。 (2认同)
  • 兰迪:很好的答案:-)。@CaitlinM.Shaw(和 Randi):这是一个很好的问题。是的,有一种方法可以告诉。如果您发布该问题,然后在此处的回复评论中发布指向该问题的链接(请务必使用我的 `@` 名称,以便我收到通知),我将发布答案。 (2认同)

Han*_*dyD 5

根据关于压缩的文档文章:

在堆重建之前,作为 DML 操作的一部分在堆中分配的新页面不使用 PAGE 压缩。通过移除和重新应用压缩,或者通过创建和移除聚集索引来重建堆。

这似乎与您所看到的一致。似乎在重建它之前,您实际上并没有在桌子上获得压缩。您可以尝试在未压缩的表上加载数据,看看您是否仍然平均每页 17 行,或者是否会减少。如果它保持不变,那么您就没有得到压缩,并且需要重建。

您还可以向表中添加聚集索引,这应该可以防止您的表在批量加载数据后被解压缩/低压缩。