【问题标题】:Count(*) differs from rows in sys.partitionsCount(*) 与 sys.partitions 中的行不同
【发布时间】:2014-08-13 07:18:01
【问题描述】:

我正在使用以下查询来获取有关数据库中所有表的信息:

SELECT 
    t.NAME AS TableName,
    i.name as indexName,
    sum(p.rows) as RowCounts,
    sum(a.total_pages) as TotalPages, 
    sum(a.used_pages) as UsedPages, 
    sum(a.data_pages) as DataPages,
    (sum(a.total_pages) * 8) / 1024 as TotalSpaceMB, 
    (sum(a.used_pages) * 8) / 1024 as UsedSpaceMB, 
    (sum(a.data_pages) * 8) / 1024 as DataSpaceMB
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
WHERE 
    t.NAME NOT LIKE 'dt%' AND
    i.OBJECT_ID > 255 AND   
    i.index_id <= 1
GROUP BY 
    t.NAME, i.object_id, i.index_id, i.name 
ORDER BY 
    object_name(i.object_id)

问题在于,对于某些表,它报告的行数与我报告的不同:

select count(*) FROM someTable

为什么会这样?

编辑:

第一个查询返回更高的计数:

First: 1 240 464
Second:  413 496

【问题讨论】:

    标签: sql sql-server database-schema


    【解决方案1】:

    问题是每个分区有多个 allocation_unit,所以同一个分区可以出现多次,因此 sum(p.rows) 最终会多次计算同一个分区,所以你得到的倍数正确的行数。

    这是我解决问题的方法: (请注意,我的查询与您的不同,我的列略有不同,使用的是 Kb 而不是 Mb,但思路是一样的)

        SELECT 
            s.Name + '.' + t.name AS table_name,
            (select sum(p2.rows)
                from sys.indexes i2 inner join sys.partitions p2 ON i2.object_id = p2.OBJECT_ID AND i2.index_id = p2.index_id
                where i2.object_id = t.object_id and i2.object_id > 255 and (i2.index_id = 0 or i2.index_id = 1)
            ) as total_rows,
            SUM(CASE WHEN (i.index_id=0) OR (i.index_id=1) THEN a.total_pages * 8 ELSE 0 END) AS data_size_kb,
            SUM(CASE WHEN (i.index_id=0) OR (i.index_id=1) THEN a.used_pages * 8 ELSE 0 END) AS data_used_kb,
            SUM(CASE WHEN (i.index_id=0) OR (i.index_id=1) THEN 0 ELSE a.total_pages * 8 END) AS index_size_kb,
            SUM(CASE WHEN (i.index_id=0) OR (i.index_id=1) THEN 0 ELSE a.used_pages * 8 END) AS index_used_kb,
            SUM(a.total_pages) * 8 AS total_size_kb, 
            SUM(a.used_pages) * 8 AS total_used_kb,
            SUM(a.used_pages) * 100 / CASE WHEN SUM(a.total_pages) = 0 THEN 1 ELSE SUM(a.total_pages) END AS percent_full
        FROM 
            sys.tables t
        INNER JOIN 
            sys.schemas s ON s.schema_id = t.schema_id
        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
        WHERE 
            t.is_ms_shipped = 0 AND i.OBJECT_ID > 255 
        GROUP BY 
            t.object_id, t.Name, s.Name
        ORDER BY SUM(a.total_pages) DESC
    

    【讨论】:

      【解决方案2】:

      来自the sys.partitions documentation

      rows bigint 此分区中的大致行数。

      (强调我的)。系统视图不会在表中保留准确的行数。想想这会带来什么,以及它会给所有插入/删除语句增加多少开销。如果我是一个赌徒,我会说它是在计算聚集索引或堆中的页数,这是一个便宜得多的操作。不过,这纯粹是推测性的。

      【讨论】:

      • 这个答案是不正确的:计数确实是近似的,但真正的问题是查询是错误的,因为它导致同一个分区的行被多次求和,所以你得到了正确的(如果近似)答案乘以存在的分配单元数量。
      • 如果给定分区有多个 AU(即 IN_ROW_DATA 和 LOB_DATA),则可能会出现重复计算。一个简单的解决方法是在原始查询中添加一个谓词,只针对 IN_ROW_DATA 或对找到的所有 AU 进行平均。但是说它不正确有点强。关于 sys.partitions 中的行数是一个近似值,我的原始答案中的所有内容都是正确的。
      • 确实,您评论中的信息是正确的,但鉴于原始问题引用了 sys.partitions 的查询,当实际计数为 413496(分区的近似性质)时给出的答案为 1240464。 rows 不是对提问者所见内容的解释。无论如何我都赞成你的回答,因为它仍然是很好的信息。
      【解决方案3】:

      您是否查看过有关sys.allocation_units 视图的帮助文章?显然,container_id 字段比看起来要多一些。尝试将此添加到where 部分:

      and a.type = 2
      

      【讨论】:

      • 这是正确的见解,但不是正确的解决方案。因为每个分区可以有多个 allocation_unit,所以同一个分区可以出现多次,因此 sum(p.rows) 最终会多次计算同一个分区。
      【解决方案4】:

      在 SQL Server 2016 中,为了修复 count(*)sys.partitions 不匹配,我对主键执行了索引重建。幸运的是,TABLE 只有 240 万行,所以没有花那么长时间,因为我有标准版,所以无法在线重建。

      【讨论】:

        【解决方案5】:

        内连接会导致不匹配的行被过滤掉。组也会影响您的行数,因为它们可以组合行。这两个条件导致聚合查询的行计数低于简单计数(*)。

        我看到您具体询问的是 sys.partitions 表。可能的解释是,在 i.object_id = p.OBJECT_ID 和 i.index_id = p.index_id 的匹配条件下,sys.indexes 表中的每一行并不匹配。尝试运行:

        Select 
          count(*) 
        from 
          sys.partitions p
        LEFT JOIN
          sys.indexes i ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
        

        然后您可能会看到您期望的计数。去掉 count 函数,只需要Select * ... 就能找到不匹配的行。

        【讨论】:

        • 问题是反过来了。
        • 您看到更高的行数?在这种情况下,sys.indexes 表中必须存在重复项。将我发布的查询更改为右连接,您应该会看到它们。
        猜你喜欢
        • 2016-10-20
        • 2012-06-22
        • 1970-01-01
        • 2023-03-24
        • 1970-01-01
        • 2014-12-08
        • 1970-01-01
        • 2012-04-25
        • 2022-11-03
        相关资源
        最近更新 更多