添加SPARSE使表更大


9

我有一个通用的日志表,大约有500万行。
有一个“强类型”字段存储事件类型,还有一堆“弱类型”列包含与事件相关的数据。也就是说,那些“错误输入”列的含义取决于事件的类型。

这些列定义为:

USER_CHAR1 nvarchar(150) null,
USER_CHAR2 nvarchar(150) null,
USER_CHAR3 nvarchar(150) null,
USER_CHAR4 nvarchar(150) null,
USER_CHAR5 nvarchar(150) null,

USER_INTEGER1 int null,
USER_INTEGER2 int null,
USER_INTEGER3 int null,
USER_INTEGER4 int null,
USER_INTEGER5 int null,

USER_FLAG1 bit null,
USER_FLAG2 bit null,
USER_FLAG3 bit null,
USER_FLAG4 bit null,
USER_FLAG5 bit null,

USER_FLOAT1 float null,
USER_FLOAT2 float null,
USER_FLOAT3 float null,
USER_FLOAT4 float null,
USER_FLOAT5 float null

每种类型的第1列和第2列都被大量使用,但是从第3列开始,很少有事件类型会提供这么多的信息。因此,我倾向于将每种类型的3-5列标记为SPARSE

我首先进行了一些分析,发现确实,每个列中至少有80%的数据是null,而在100%的数据中是null。根据40%储蓄门槛表SPARSE这将是他们的一个巨大胜利。

因此,我去SPARSE了每组中的3-5列。现在,我的表占用了大约1.8Gb的数据空间sp_spaceused,而稀疏之前为1Gb。

我尝试过dbcc cleantable,但是没有效果。
然后dbcc shrinkdatabase,也没有效果。

感到困惑,我删除SPARSE并重复了dbccs。该表的大小保持在1.8Gb。

是什么赋予了?


将尝试并繁殖。万一它有什么区别,表是堆还是有聚簇索引?
马丁·史密斯

@MartinSmith是否具有聚集索引rowid int not null identity(1,1) primary key clustered
Gserg

Answers:


14

使列稀疏后,您需要重建聚簇索引。删除的列仍会存在于数据页中,直到您执行此操作为止,这可以通过针对sys.system_internals_partition_columns或使用查询来看到DBCC PAGE

SET NOCOUNT ON;
CREATE TABLE Thing 
(
ThingId int IDENTITY CONSTRAINT PK PRIMARY KEY,
USER_CHAR1 nvarchar(150) null,
USER_CHAR2 nvarchar(150) null,
USER_CHAR3 nvarchar(150) null,
USER_CHAR4 nvarchar(150) null,
USER_CHAR5 nvarchar(150) null
)
INSERT INTO Thing
SELECT REPLICATE('A',150),
       CASE WHEN number % 5 = 1 THEN REPLICATE('A',150) END,
       CASE WHEN number % 5 = 2 THEN REPLICATE('A',150) END,
       CASE WHEN number % 5 = 3 THEN REPLICATE('A',150) END,              
       CASE WHEN number % 5 = 4 THEN REPLICATE('A',150) END
FROM master..spt_values   

EXEC sp_spaceused 'Thing'

ALTER TABLE dbo.Thing ALTER COLUMN USER_CHAR2 ADD SPARSE
ALTER TABLE dbo.Thing ALTER COLUMN USER_CHAR3 ADD SPARSE
ALTER TABLE dbo.Thing ALTER COLUMN USER_CHAR4 ADD SPARSE
ALTER TABLE dbo.Thing ALTER COLUMN USER_CHAR5 ADD SPARSE

DECLARE @DynSQL NVARCHAR(MAX);

SELECT @DynSQL =  'DBCC TRACEON (3604);
                   DBCC PAGE(0, ' + LEFT(file_id,10) + ', ' + LEFT(page_id,10) + ', 3); 
                   DBCC TRACEOFF(3604); ' 
FROM Thing
CROSS APPLY sys.fn_PhysLocCracker(%%physloc%%)
WHERE ThingId=76

EXEC(@DynSQL)    

SELECT pc.*
FROM sys.system_internals_partition_columns pc
JOIN sys.partitions p on p.partition_id=pc.partition_id
WHERE p.object_id = object_id('Thing')
AND pc.is_dropped=1

 EXEC sp_spaceused 'Thing'

ALTER INDEX PK ON Thing REBUILD;    

SELECT @DynSQL =  'DBCC TRACEON (3604);
                   DBCC PAGE(0, ' + LEFT(file_id,10) + ', ' + LEFT(page_id,10) + ', 3); 
                   DBCC TRACEOFF(3604); ' 
FROM Thing
CROSS APPLY sys.fn_PhysLocCracker(%%physloc%%)
WHERE ThingId=76

EXEC(@DynSQL)    

SELECT pc.*
FROM sys.system_internals_partition_columns pc
JOIN sys.partitions p on p.partition_id=pc.partition_id
WHERE p.object_id = object_id('Thing')
AND pc.is_dropped=1

EXEC sp_spaceused 'Thing'

DROP TABLE Thing 

1
太棒了 我们是否应该将其视为文档中的错误?“ SQL Server数据库引擎使用以下过程来完成此更改:1)以新的存储大小和格式向表中添加新列。2)对于表中的每一行,更新并复制旧表中存储的值列到新列。3)从表架构中删除旧列。4)重建表以回收旧列使用的空间。
GSerg 2012年

3
@GSerg-嗯,对。同意,似乎第4点是不正确的。假设您要对12列进行此操作,那么您不希望每个列都隐式进行重建,尽管看起来行为是正确的,但文档不是。
马丁·史密斯

1
@SQLKiwi-谢谢。完成
马丁·史密斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.