表LogCount
:
Column1 int not null
Column2 int not null
Column3 int not null
[Date] datetime not null
[Count] float(53) not null
该表包含约 8600 万行并具有以下索引:
alter table LogCount add constraint PK_LogCount_Id primary key clustered
( [Date], Column1, Column2, Column3 )
go
create nonclustered index IX_Column2_Date on LogCount ( Column2, [Date] )
include ( [Count] )
go
sp_spaceused
给出以下内容:
name rows reserved data index_size unused
LogCount 85800181 8089216 KB 4226664 KB 3860968 KB 1584 KB
该Count
列不会也永远不会存储浮点数,只存储整数,所以我将其更改为smallint
(我预计)每行将节省 6 个字节(float(53)
= 8 个字节,smallint
= 2 个字节):
drop index LogCount.IX_Column2_Date
go
alter table LogCount alter column [Count] smallint not null
go
create nonclustered index IX_Column2_Date on LogCount ( Column2, [Date] )
include ( [Count] )
go
然后我重新运行sp_spaceused
:
name rows reserved data index_size unused
LogCount 85800181 7670848 KB 5255528 KB 2414496 KB 824 KB
正如预期的那样,索引大小急剧下降,但数据大小却增加了 1 GB!
然后我重新运行上面的 drop/alter/create 语句但使用int
(4 个字节)并得到以下结果:
name rows reserved data index_size unused
LogCount 85800181 7848032 KB 5255528 KB 2591688 KB 816 KB
然后我尝试了float(1)
(也是 4 个字节):
name rows reserved data index_size unused
LogCount 85800181 7848016 KB 5255528 KB 2591672 KB 816 KB
最后我回到原来的float(53)
:
name rows reserved data index_size unused
LogCount 85800181 10680584 KB 7726896 KB 2952464 KB 1224 KB
与原始版本相比,数据大小增加了约 3.3 GB(几乎翻了一番),而索引大小减少了约 900MB。
一位同事建议罪魁祸首可能是 MSSQL 正在为alter column
语句分配额外的页面并且之后没有释放它们,所以我也尝试dbcc shrinkdatabase
在每一步之后执行,但结果是一样的。
所以我的问题:
- 为什么将列从较大数据类型更改为较小数据类型会导致使用更多数据空间?
sp_spaceused
可靠吗?如果不是,我应该使用什么来代替?如果没有更好的选择,我如何确定更改列的数据类型是否会对磁盘空间使用产生正面或负面影响?
更改列的数据类型时,SQL Server 将选择:
即使必须更改每一行,SQL Server 仍会采取措施(在可能的情况下)将速度优先于最终大小,因为我们希望 DDL 更改尽快完成。优化存储空间可以等待维护窗口。
将float更改为smallint可以容纳在为该行分配的现有空间内,但它确实会留下一些未使用的空间。如前所述,这可以通过完全重建更改后的结构来恢复。
您需要运行
ALTER TABLE ... REBUILD
以释放 DDL 修改未使用的空间。有关详细信息,请参阅 MSDN,网址为https://msdn.microsoft.com/en-us/library/ms190273.aspx?f=255&MSPPError=-2147217396