【问题标题】:DBCC CLEANTABLE is not freeing used spaceDBCC CLEANTABLE 未释放已用空间
【发布时间】:2016-04-05 09:07:28
【问题描述】:

我有大约 300 张桌子,每张桌子有 5.5kk 行。其中一行使用 nvarchar(128) 作为数据类型 (SQL Server 2012)。
我们决定将其更改为 int 并将 FK 添加到包含所有 nvarchars 的字典表中。

完成所有操作后,我删除了 nvarchar 列,但表的大小保持不变。 我使用 DBCC CLEANTABLE 并重建索引以回收可用空间,但表的大小仍然没有改变。

到目前为止,我发现的唯一方法是将所有数据复制到新表中。

问题:我在这里缺少什么?为什么空间仍被标记为已使用并且我无法通过收缩或 CREANTABLE 命令释放?

谢谢!

回答:看起来答案很简单,由于我缺乏知识,我无法找到它。 这里的主要问题是堆碎片。这个查询对我有用:

ALTER TABLE [TABLE_NAME] REBUILD

我不确定这是不是最好的方法,但至少它是一种可行的方法。

已编辑1: 抱歉,我想我忘了提 - 我在 Text 字段上有聚集索引,所以我必须删除索引才能真正删除该字段。现在我没有索引了。

已编辑2

旧表:

CREATE TABLE [dbo].[Data_](
    [ID] [bigint] PRIMARY KEY IDENTITY(1,1) NOT NULL,
    [Text] [nvarchar](128) NOT NULL,
    [Category] [tinyint] NOT NULL,
    [Country] [nvarchar](2) NOT NULL,
    [ImportTimestamp] [date] NOT NULL
)

新表:

CREATE TABLE [dbo].[Data_New](
    [ID] [int] PRIMARY KEY IDENTITY(1,1) NOT NULL,
    [Category] [tinyint] NOT NULL,
    [Country] [nvarchar](2) NOT NULL,
    [TextID] [int] NOT NULL)

ALTER TABLE [dbo].[Data_New]  WITH CHECK ADD FOREIGN KEY([TextID])
REFERENCES [dbo].[Dictionary] ([Id])

复制脚本:

INSERT INTO Data_New
      ([Category]
      ,[Country]
      ,[TextID])
SELECT 
      [Category]
      ,[Country]
      ,[TextID]
  FROM Data_

【问题讨论】:

    标签: sql sql-server tsql sql-server-2012 dbcc


    【解决方案1】:

    您确定表会因为此列更改而变小吗? nvarchar(128) 并不意味着 SQL Server 将分配 128 个字节 (+2) 来保存数据,例如字符串“test”。字符串 'test' 只占用 6 个字节。也许您需要先检查表中的可用空间:

    SELECT 
        t.NAME AS TableName,
        s.Name AS SchemaName,
        p.rows AS RowCounts,
        SUM(a.total_pages) * 8 AS TotalSpaceKB, 
        SUM(a.used_pages) * 8 AS UsedSpaceKB, 
        (SUM(a.total_pages) - SUM(a.used_pages)) * 8 AS UnusedSpaceKB
    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 = 'TABLE_NAME'
        AND t.is_ms_shipped = 0'
        AND i.OBJECT_ID > 255 
    GROUP BY 
        t.Name, s.Name, p.Rows
    ORDER BY 
        t.Name
    

    【讨论】:

    • 如果他将数据复制到新表中,则空间被回收。所以这不是问题。
    • 问题是空间浪费。他的陈述(希望是真的)证明了它的存在,即将数据复制到新表会缩小它。
    • 很清楚。但他仍然需要先检查表内是否有空闲空间
    • 我现在明白你的意思是旧表可能有部分空页。但是,此查询不会确定在部分填充的页面上浪费的空间。仍然,一个有效的观点。 +1
    • 所有保存的字符串大约是 100-120 ch。长度。所以我确信那里有浪费的空间。根据您的查询,我得到了 900 中 12 mb 的免费结果。这不可能是真的。重新创建表后,我将大小从 900 减小到 200 mb。
    【解决方案2】:

    如果您不关心理解问题而只想摆脱它,那么重建索引是消除任何浪费的可靠方法。这是因为索引重建重新构建了物理数据结构。无论包含什么旧索引都不再重要。

    将其与 CLEANTABLE 进行比较时需要权衡取舍。如果重建可以为您完成这项工作,我会一直这样做,因为它是一个非常完整的解决方案。

    当我在这个答案中说“索引”时,我的意思是所有包含您关心的列之一的物理结构。这可以是 b 树索引或表所基于的堆。

    【讨论】:

    • 那不覆盖整个表的数据?或者您的意思是重建聚集索引(如果可用)?
    • 他可以重建任何他想要的东西,例如单独的每个索引。大多数表都有 CI,因此他需要重建包含他关心的列之一的所有索引。如果它是一个堆,他需要重建堆,这会导致其他所有东西也被重建。有了这个简单的答案,我希望简单地重建一切对他来说是可以的。如果不是,那么答案不适用于 OP,但可能适用于未来的访问者。
    • 好吧,我试图理解这个问题,但我只有一个用于搜索的字段的索引,不,我实际上正在删除这个字段,我删除了当前索引,因为它不再有效。而且我不敢相信我有 900 mb 的表,而一个索引占用了 700 mb。
    • @DmytroAleksin 我不确定情况。您是否重建了包括表(聚集索引或堆)在内的所有内容?如果这样做没有释放空间,就会发生完全不同的事情。
    • 谢谢!实际上我不知道堆碎片的事实。 ALTER TABLE ...REBUILD 为我完成了这项工作,但我不确定它是否是这种情况下的最佳解决方案。
    猜你喜欢
    • 2015-11-07
    • 1970-01-01
    • 1970-01-01
    • 2012-12-28
    • 2020-04-19
    • 2018-05-27
    • 2012-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多