You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL单值列610万行存储疑问:修改后空间节省为何有限?

问题背景

我有一个SQL表,通过以下SQL语句查询表大小与行数:

SELECT 
    t.name AS TableName,
    s.name AS SchemaName,
    p.rows,
    CAST(ROUND(((SUM(a.total_pages) * 8) / 1024.00), 2) AS NUMERIC(36, 2)) AS TotalSpaceMB,
    getdate() as [Time]
into #temp
FROM 
    sys.tables t
INNER JOIN      
    sys.indexes i ON t.object_id = i.object_id
INNER JOIN 
    sys.partitions p ON i.object_id = p.object_id AND i.index_id = p.index_id
INNER JOIN 
    sys.allocation_units a ON p.partition_id = a.container_id
LEFT OUTER JOIN 
    sys.schemas s ON t.schema_id = s.schema_id
WHERE 
    t.name NOT LIKE 'dt%' 
    AND t.is_ms_shipped = 0
    AND i.object_id > 255 
GROUP BY 
    t.name, s.name, p.rows
ORDER BY 
    TotalSpaceMB DESC, t.name

select * from #temp

查询结果显示目标表有610万行,表大小2384.77MB。该表某列的610万行均为33字符的相同值,我将数据复制到新表后执行如下修改:

select * into  table_of_interest_temp from table_of_interest
update table_of_interest_temp
set Key = 1
where Key = '6cd191449a764432ac3e-ab1c12f900dd'

重新运行表大小查询后,新表仅减少6.31MB,变为2378.46MB。我原本预期能节省更多空间,因为修改另一含约2000个不同值的列时,空间节省效果显著,请问这一现象的原因是什么?

原因分析
  • 列数据类型的限制:如果Key列是固定长度类型(比如CHAR(33)),不管实际存储的字符长度是多少,都会占用定义的33字符空间,修改为1后单条记录的存储空间没有变化;如果是可变长度类型(比如VARCHAR(33)),单条记录确实能省空间,但SQL Server不会自动释放数据页中的空闲空间,这些空闲空间仍会被计入表的总分配空间,只有当页被完全清空时才会回收。
  • 索引的差异影响:之前修改的含2000个不同值的列大概率带有索引,修改后重复值增多,索引的叶节点会因为重复值的高压缩率大幅减少空间占用;而这次的Key列如果没有索引,或者原本全相同值的索引已经被压缩到极致,修改后索引空间几乎无变化,自然总空间节省有限。
  • 页压缩的特性:如果表开启了页压缩,原来全相同的33字符值已经被压缩到最优状态——页压缩会把重复值只存储一次,其余行用引用指向该值。改成1后,压缩率提升的空间极小,因此整体空间节省微乎其微。而之前的列修改前重复率低,修改后重复率大幅提升,压缩效果显著,所以空间节省明显。
  • 空间统计的逻辑:通过sys.allocation_units查询到的是表已分配的空间,而非实际使用的空间。SELECT * INTO创建的新表继承了原表的页分配,修改后的数据页并未被重新整理,已分配的页即使有空余空间,也会被计入总大小,不会立即减少。

内容的提问来源于stack exchange,提问作者frank

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 22:12:45