计算非常大的表中的确切行数的最快方法?

Swa*_*rma 221 sql database

我遇到的文章表明,SELECT COUNT(*) FROM TABLE_NAME当表有很多行和很多列时会很慢.

我有一个表可能包含数十亿行[它有大约15列].有没有更好的方法来获得表的行数的精确计数?

请在回答之前考虑以下事项:

  • 我正在寻找独立于数据库供应商的解决方案.如果它涵盖MySQL,Oracle,MS SQL Server,那就没关系.但如果确实没有数据库供应商独立解决方案,那么我将为不同的数据库供应商寻求不同的解决方案.

  • 我不能使用任何其他外部工具来做到这一点.我主要是在寻找基于SQL的解决方案.

  • 我无法进一步规范我的数据库设计.它已经在3NF,而且已经编写了很多代码.

gbn*_*gbn 231

简单回答:

  • 数据库供应商独立解决方案=使用标准= COUNT(*)
  • 大致的 SQL Server解决方案,但不使用COUNT(*)=超出范围

笔记:

COUNT(1)= COUNT(*)= COUNT(PrimaryKey)以防万一

编辑:

SQL Server示例(14亿行,12列)

SELECT COUNT(*) FROM MyBigtable WITH (NOLOCK)
-- NOLOCK here is for me only to let me test for this answer: no more, no less
Run Code Online (Sandbox Code Playgroud)

1次运行,5:46分钟,计数= 1,401,659,700

--Note, sp_spaceused uses this DMV
SELECT
   Total_Rows= SUM(st.row_count)
FROM
   sys.dm_db_partition_stats st
WHERE
    object_name(object_id) = 'MyBigtable' AND (index_id < 2)
Run Code Online (Sandbox Code Playgroud)

2次运行,均低于1秒,计数= 1,401,659,670

第二个有较少的行=错误.取决于写入将是相同或更多(删除是在几小时内完成的)

  • @zerkmsby:对于COUNT(键),我的意思是COUNT(primarykey),它应该是不可为空的.我会澄清一下 (14认同)
  • 不,`COUNT(*)= COUNT(键)`.这是错的.如果没有"NOT NULL"约束 - 则它们可能不相等(在结果和执行计划中). (9认同)
  • with(NOLOCK)不允许它在生产中运行,并且可能导致计数不准确.当您使用该提示时,确保它可以防止锁定,但生产盒上的副作用是您可以在某些情况下对行计数两次或在其他情况下跳过行.NOLOCK最好在未写入的表上使用,因为它允许"脏读".除非他们完全理解后果,否则不建议人们使用该提示 (6认同)
  • @gbn非常好的解决方案,你能说出`index_id <2`的用途吗? (5认同)
  • @mishrsud唯一准确的查询是SELECT COUNT(*),但它很慢.你可以有精确和慢,或粗糙和快速.你所做的将取决于你需要计算的目的更重要的是什么.无论出于何种原因,NO LOCK可能包含或确实排除处于事务中间或移动页面的行. (4认同)
  • 如果目标是性能(相对于准确性),我会补充说`object_id = object_id('Table')`比[object_name(object_id)='Table'更有效率**** (2认同)
  • @MartijnScheffer好吧,请在** Microsoft SQL Server **中尝试一下并进行报告。它不是“独立于数据库供应商的解决方案”,根据ANSI SQL标准,该解决方案显然适用于所有平台 (2认同)

sal*_*hra 25

到目前为止,MySQL上最快的方法是:

SHOW TABLE STATUS;
Run Code Online (Sandbox Code Playgroud)

如果需要,您将立即获得包含行数(即总数)的所有表格以及大量额外信息.

  • 这根本不起作用,例如,在INNODB上,存储引擎读取几行并推断出猜测行数 (2认同)

Jak*_*keJ 18

我从另一个 StackOverflow 问题/答案中得到了这个脚本:

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'YourTableNameHere'
  AND s.name = N'dbo'
  AND p.index_id IN (0,1);
Run Code Online (Sandbox Code Playgroud)

我的表有 5 亿条记录,上面的返回时间不到 1 毫秒。同时,

SELECT COUNT(id) FROM MyTable
Run Code Online (Sandbox Code Playgroud)

耗时整整 39 分 52 秒!

它们产生完全相同的行数(在我的例子中,正好是 519326012)。

我不知道是否会一直如此。


Den*_*rdy 10

我遇到过一些文章,指出当表有很多行和很多列时,SELECT COUNT(*)FROM TABLE_NAME会很慢.

这取决于数据库.一些加速计数,例如通过跟踪索引中的行是活的还是死的,允许仅索引扫描来提取行数.其他人则没有,因此需要访问整个表格并逐个计算实时行.一个巨大的桌子要么慢.

请注意,您通常可以通过使用查询优化工具,表统计信息等来提取良好的估计值.例如,在PostgreSQL的情况下,您可以解析输出explain count(*) from yourtable并获得相当好的行数估计.这让我想到了你的第二个问题.

我有一个表可能包含数十亿行[它有大约15列].有没有更好的方法来获得表的行数的精确计数?

真的吗?:-)你真的是指数十亿行的表中的确切数量?你真的相信吗?:-)

如果你真的这样做,你可以保留一下总的使用触发器,但如果你这样做,请注意并发和死锁.

  • "是的Denis,确切的计数是必需的.:(" - 我只能推测.数据库维护过程是否发现表A中有42,123,876行,然后在表B中创建42,123,876个空行,然后循环通过表A并更新表B中的行......?或者它比那更疯狂?;-) (6认同)
  • 幸运的是,谷歌经理比你的老板更合理......想象一下,如果它为你的每个查询返回了确切的搜索结果数而不是坚持估计数,那将会有多慢. (5认同)

jam*_*ams 9

你可以试试这个sp_spaceused(Transact-SQL)

显示当前数据库中的表,索引视图或Service Broker队列使用的行数,保留的磁盘空间和磁盘空间,或显示整个数据库保留和使用的磁盘空间.


Jes*_*ebb 9

有没有更好的方法来获得表的行数的精确计数?

简单地回答你的问题,.

如果您需要以DBMS独立的方式执行此操作,最快的方法将始终是:

SELECT COUNT(*) FROM TableName
Run Code Online (Sandbox Code Playgroud)

一些DBMS供应商可能有更快的方法,只适用于他们的系统.其中一些选项已经发布在其他答案中.

COUNT(*) 应该由DBMS(至少任何PROD值得的数据库)优化,所以不要试图绕过他们的优化.

请注意:
我确信您的许多其他查询也需要很长时间才能完成,因为您的表格大小.任何性能问题都应该通过考虑速度考虑您的架构设计来解决.我意识到你说它不是一个改变的选择,但它可能会发现10分钟以上的查询也不是一个选择.3 NF并不总是最好的方法,当你需要的速度,有时数据可以在多个表中,如果记录不就被划分具有存储在一起.需要考虑的事情......


Ali*_*dah 6

如果SQL Server版本为2005/2008,则可以使用DMV计算表中的行数:

-- Shows all user tables and row counts for the current database 
-- Remove is_ms_shipped = 0 check to include system objects 
-- i.index_id < 2 indicates clustered index (1) or hash table (0) 
SELECT o.name, 
 ddps.row_count 
FROM sys.indexes AS i 
 INNER JOIN sys.objects AS o ON i.OBJECT_ID = o.OBJECT_ID 
 INNER JOIN sys.dm_db_partition_stats AS ddps ON i.OBJECT_ID = ddps.OBJECT_ID 
 AND i.index_id = ddps.index_id 
WHERE i.index_id < 2 
 AND o.is_ms_shipped = 0 
ORDER BY o.NAME 
Run Code Online (Sandbox Code Playgroud)

对于SQL Server 2000数据库引擎,可以使用sysindexes,但强烈建议避免在SQL Server的将来版本中使用它,因为它可能会在不久的将来删除。

示例代码取自:如何快速而无痛地获取表行计数


Thi*_*rry 6

我发现这篇好文章SQL Server–HOW-TO:快速检索表的准确行数,从中martijnh1可以很好地概括每个场景。

我需要在需要根据特定条件提供计数的地方对此进行扩展,当我计算出这部分时,我将进一步更新此答案。

同时,以下是文章的详细信息:

方法一:

询问:

SELECT COUNT(*) FROM Transactions 
Run Code Online (Sandbox Code Playgroud)

注释:

执行全表扫描。在大桌子上慢。

方法二:

询问:

SELECT CONVERT(bigint, rows) 
FROM sysindexes 
WHERE id = OBJECT_ID('Transactions') 
AND indid < 2 
Run Code Online (Sandbox Code Playgroud)

注释:

检索行数的快速方法。取决于统计数据并且不准确。

运行 DBCC UPDATEUSAGE(Database) WITH COUNT_ROWS,这对于大型表来说可能需要很长时间。

方法三:

询问:

SELECT CAST(p.rows AS float) 
FROM sys.tables AS tbl 
INNER JOIN sys.indexes AS idx ON idx.object_id = tbl.object_id and
idx.index_id < 2 
INNER JOIN sys.partitions AS p ON p.object_id=CAST(tbl.object_id AS int) 
AND p.index_id=idx.index_id 
WHERE ((tbl.name=N'Transactions' 
AND SCHEMA_NAME(tbl.schema_id)='dbo')) 
Run Code Online (Sandbox Code Playgroud)

注释:

SQL 管理工作室计算行数的方式(查看表属性、存储、行数)。非常快,但仍然是大约的行数。

方法四:

询问:

SELECT SUM (row_count) 
FROM sys.dm_db_partition_stats 
WHERE object_id=OBJECT_ID('Transactions')    
AND (index_id=0 or index_id=1); 
Run Code Online (Sandbox Code Playgroud)

注释:

快速(虽然不如方法 2 快)操作和同样重要、可靠。


小智 5

我用

select /*+ parallel(a) */  count(1) from table_name a;
Run Code Online (Sandbox Code Playgroud)


joh*_*ins 5

我已经远远不及那些已经回答的其他人,但是我遇到的问题是我用来从表中选择一个随机行(不过度相关),但我需要知道我的参考表中的行数计算随机指数.使用传统的Count(*)或Count(1)工作,但偶尔我的查询运行时间最长为2秒.所以相反(我的名为'tbl_HighOrder'的表)我正在使用:

Declare @max int

Select @max = Row_Count
From sys.dm_db_partition_stats
Where Object_Name(Object_Id) = 'tbl_HighOrder'
Run Code Online (Sandbox Code Playgroud)

它工作得很好,Management Studio中的查询时间为零.


Kal*_*ist 5

好吧,晚了5年,不确定是否有帮助:

我试图数数。MS SQL Server Management Studio的SQL Server表中的行数,并遇到一些溢出错误,然后使用以下代码:

从[dbname]。[dbo]中选择count_big(1)。[FactSampleValue];

结果 :

24296650578行