43

统计大量数据的表可能会很慢,有时需要几分钟;它还可能在繁忙的服务器上产生死锁。我想显示真实值,NOLOCK 不是一个选项。

我使用的服务器是 SQL Server 2005 或 2008 Standard 或 Enterprise - 如果重要的话。我可以想象 SQL Server 维护每个表的计数,如果没有 WHERE 子句,我可以很快得到这个数字,对吧?

例如:

SELECT COUNT(*) FROM myTable

应该立即返回正确的值。我需要依靠统计数据来更新吗?

4

5 回答 5

76

非常接近的近似值(忽略任何进行中的交易)将是:

SELECT SUM(p.rows) FROM sys.partitions AS p
  INNER JOIN sys.tables AS t
  ON p.[object_id] = t.[object_id]
  INNER JOIN sys.schemas AS s
  ON s.[schema_id] = t.[schema_id]
  WHERE t.name = N'myTable'
  AND s.name = N'dbo'
  AND p.index_id IN (0,1);

这将比返回快得多COUNT(*),并且如果您的表变化得足够快,那么它实际上并没有降低准确度 - 如果您的表在您开始 COUNT (并且锁定)和返回时(锁定时)之间发生了变化被释放并且所有等待的写事务现在都被允许写入表),它更有价值吗?我不这么认为。

如果您有要计算的表的某个子集(例如WHERE some_column IS NULL,在较小的集合上创建过滤索引)。所以这两个索引之一:

CREATE INDEX IAmTheException ON dbo.table(some_column)
  WHERE some_column IS NULL;

CREATE INDEX IAmTheRule ON dbo.table(some_column)
  WHERE some_column IS NOT NULL;

然后你可以使用类似的方式获得计数:

SELECT SUM(p.rows) FROM sys.partitions AS p
  INNER JOIN sys.tables AS t
  ON p.[object_id] = t.[object_id]
  INNER JOIN sys.schemas AS s
  ON s.[schema_id] = t.[schema_id]
  INNER JOIN sys.indexes AS i
  ON p.index_id = i.index_id
  WHERE t.name = N'myTable'
  AND s.name = N'dbo'
  AND i.name = N'IAmTheException' -- or N'IAmTheRule'
  AND p.index_id IN (0,1);

如果你想知道相反的情况,你只需从上面的第一个查询中减去。

于 2012-09-18T15:23:50.487 回答
12

(“大量数据”有多大?-应该先评论一下,但也许下面的 exec 已经帮助你了)

如果我在我的开发机器上在 15 秒内对具有 2 亿行和 COUNT(*) 的静态表运行查询(意味着在很长一段时间内没有其他人对读/写/更新感到恼火,所以争用不是问题)(甲骨文)。考虑到纯数据量,这仍然很快(至少对我来说)

正如您所说,NOLOCK 不是一种选择,您可以考虑

exec sp_spaceused 'myTable'

也是。

但这与 NOLOCK 几乎相同(忽略争用 + 删除/更新 afaik)

于 2012-09-18T15:09:40.233 回答
4

我使用 SSMS 已有十多年了,直到去年才发现它可以快速轻松地为您提供这些信息,这要归功于这个答案

  1. 从数据库树(对象资源管理器)中选择“表”文件夹
  2. 按 F7 或选择View > Object Explorer Details以打开 Object Explorer Details 视图
  3. 在此视图中,您可以右键单击列标题以选择要查看的列,包括已使用的表空间、已使用的索引空间和行数: 在此处输入图像描述

请注意,Azure SQL 数据库对此的支持充其量似乎有点参差不齐——我的猜测是来自 SSMS 的查询超时,因此每次刷新它只返回少数表,但似乎总是返回突出显示的表。

于 2019-05-16T06:11:01.720 回答
2

Count 将执行表扫描或索引扫描。因此,对于大量行,它会很慢。如果你经常做这个操作,最好的办法是把计数记录保存在另一个表中。

但是,如果您不想这样做,则可以创建一个虚拟索引(查询不会使用该索引)并查询它的项目数,例如:

select 
    row_count
from sys.dm_db_partition_stats as p
inner join sys.indexes as i 
  on p.index_id = i.index_id
  and p.object_id = i.object_id
where   i.name = 'your index'

我建议创建一个新索引,因为这个索引(如果不使用)在其他操作期间不会被锁定。

正如 Aaron Bertrand 所说,维护查询可能比使用现有查询更昂贵。所以选择权在你。

于 2012-09-18T15:25:01.430 回答
-4

如果您只需要粗略计算行数,即。要确保正确加载表或确保未删除数据,请执行以下操作:

MySQL> connect information_schema;
MySQL> select table_name,table_rows from tables;
于 2017-06-23T13:30:41.637 回答