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

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

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

在回答之前请考虑以下几点:

  • 我正在寻找一个数据库供应商独立解决scheme。 如果它覆盖MySQLOracleMS SQL Server ,那就行了。 但是,如果真的没有数据库供应商独立的解决scheme,那么我将为不同的数据库供应商解决不同的解

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

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

简单的回答:

  • 数据库厂商独立解决scheme=使用标准= COUNT(*)
  • 近似的 SQL Server解决scheme,但不使用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 

1分钟,5:46分钟,计数= 1401659700

 --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) 

2次运行,都在1秒以内,计数= 1,401,659,670

第二个有更less的行=错误。 将会是相同或更多取决于写入(删除是在这里用小时完成的)

MySQL上最快的方法是:

 SHOW TABLE STATUS; 

你会立即得到所有的行数(这是总数),以及大量额外的信息,如果你想。

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

这取决于数据库。 一些加速计数,例如通过跟踪索引中的行是活的还是死的,允许仅索引扫描来提取行数。 其他人则不需要,因此需要访问整个桌面并逐一计数活动行。 要么是一张巨大的桌子会很慢。

请注意,您通常可以使用查询优化工具,表格统计等提取一个好的估计值。例如,在PostgreSQL的情况下,您可以parsingexplain count(*) from yourtableexplain count(*) from yourtable的输出,并获得相当好的估计行数。 这使我想到你的第二个问题。

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

真的吗? :-)你真的是指从一张有数十亿行的表中确切的计数? 你确定吗? 🙂

如果你真的这样做,你可以保留使用触发器的总数的痕迹,但是如果你这样做的话,要注意并发性和死锁。

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

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

我用

 select /*+ parallel(a) */ count(1) from table_name a; 

我远远没有像其他谁已经回答了专家,但我有一个问题,我正在使用从表中select一个随机行(不是太相关),但我需要知道我的参考表来计算随机指标。 使用传统的计数(*)或计数(1)的工作,但我偶尔会长达2秒查询运行。 所以相反(我的表名为“tbl_HighOrder”)我使用:

 Declare @max int Select @max = Row_Count From sys.dm_db_partition_stats Where Object_Name(Object_Id) = 'tbl_HighOrder' 

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

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

要简单回答你的问题,

如果你需要一个DBMS独立的方式来做到这一点, 最快的方法将是:

 SELECT COUNT(*) FROM TableName 

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

COUNT(*)应该由DBMS优化(至less任何PROD值得DB),所以不要试图绕过他们的优化。

在旁注:
我相信你的其他许多查询也需要很长时间才能完成,因为你的表格大小。 任何性能问题都应该可以通过思考你的模式devise来解决。 我意识到你说,这是不是一个选项来改变,但可能会发现10分钟以上的查询也不是一个选项。 当你需要速度的时候,第三NF并不总是最好的方法,如果logging不需要一起存储的话,有时候数据可以分成几个表格。 有些事情要考虑…

我不认为有一个总是最快的解决scheme:有些RDBMS /版本对SELECT COUNT(*)有特定的优化,使用更快的选项,而其他的只是表扫描。 你需要去第二组文档/支持网站,这可能需要一些更具体的查询来写,通常是以某种方式打索引。

编辑:

这里有一个想法可能工作,这取决于你的模式和数据分布:你有一个索引列,引用一个递增的值,一个数字增加的ID,甚至是一个时间戳或date? 然后,假设删除不会发生,应该可以将计数存储到最近的值(昨天的date,最近一个样本点的最高ID值),并将计数加到这个值以外,这样就可以在索引中很快解决。 当然,它非常依赖于值和索引,但适用于几乎任何版本的任何DBMS。

那么晚了5年,不确定是否有帮助:

我正在努力数数。 使用MS SQL Server Management Studio的SQL Server表中的行,并遇到一些溢出错误,然后我使用下面的:

select count_big (1)FROM [dbname]。[dbo]。[FactSampleValue];

结果 :

24296650578行

如果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 

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

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

不完全是一个DBMS不可知的解决scheme,但至less你的客户端代码不会看到差异…

用一行和一个整数字段N 1创build另一个表T,并创build刚刚执行的INSERT TRIGGER:

 UPDATE T SET N = N + 1 

另外创build一个DELETE TRIGGER来执行:

 UPDATE T SET N = N - 1 

一个值得它的盐的DBMS将保证2以上的操作的primefaces性,N将始终包含精确的行数,然后通过简单地超快速获得:

 SELECT N FROM T 

虽然触发器是特定于DBMS的,但从T中select不是,您的客户端代码将不需要为每个支持的DBMS更改。

但是,如果表是INSERT或DELETE密集型的,那么这可能会产生一些可伸缩性问题,特别是在INSERT / DELETE之后没有立即执行COMMIT的情况下。


1这些名字只是占位符 – 在生产中使用更有意义的东西。

2只要读取和写入都在一个SQL语句中完成,就不能通过读取和写入到N的并发事务来改变。

一个字面上疯狂的答案,但如果你有某种types的复制系统设置(对于一个十亿行的系统,我希望你这样做),你可以使用一个粗略估计器(如MAX(pk) ),您拥有的从站数量,并行运行多个查询。

在大多数情况下,你会根据最好的键(或者我猜的主键)将查询划分到奴隶上,这样(我们将使用250000000作为行/奴隶):

 -- First slave SELECT COUNT(pk) FROM t WHERE pk < 250000000 -- Ith slave where 2 <= I <= N - 1 SELECT COUNT(pk) FROM t WHERE pk >= I*250000000 and pk < (I+1)*250000000 -- Last slave SELECT COUNT(pk) FROM t WHERE pk > (N-1)*250000000 

但是你只需要SQL。 真是太棒了 好吧,让我们假设你是一个施虐受虐者。 在主(或最接近的奴隶)你最有可能需要创build一个表:

 CREATE TABLE counter_table (minpk integer, maxpk integer, cnt integer, slaveid integer) 

所以不是只有在你的奴隶select运行,你必须做一个插入,类似于这样的:

 INSERT INTO counter_table VALUES (I*25000000, (I+1)*250000000, (SELECT COUNT(pk) FROM ... ), @@SLAVE_ID) 

你可能会遇到奴隶写在主人桌上的问题。 你可能需要更悲伤 – 我的意思是创造性的:

 -- A table per slave! INSERT INTO counter_table_slave_I VALUES (...) 

最终应该有一个从复制图所遍历的path上的最后一个从属节点,相对于第一个从节点。 那奴隶现在应该有所有其他的计数器值,并应该有自己的价值观。 但是当你完成的时候,可能会添加行,所以你必须插入另一个补偿logging的最大值在你的counter_table和当前的最大值。

在这一点上,你必须做一个聚合函数来确定总行数是多less,但是这样做更容易,因为你最多只能在“你拥有和改变的从属数量”行上运行它。

如果你在奴隶中有单独的表格的情况,你可以使用UNION来获得你需要的所有行。

 SELECT SUM(cnt) FROM ( SELECT * FROM counter_table_slave_1 UNION SELECT * FROM counter_table_slave_2 UNION ... ) 

或者你知道,要less一点疯狂,把你的数据迁移到分布式处理系统,或者使用数据仓库解决scheme(这将给你未来太棒的数据处理)。

请注意,这取决于您的复制设置得如何。 由于主要瓶颈将很可能是持久性存储,如果你有恶劣的存储或隔离数据存储很差的邻居噪声,这可能会比只等待一个SELECT COUNT(*) ...慢慢运行SELECT COUNT(*) ...

但是,如果你有很好的复制,那么你的速度收益应该直接与数量或奴隶有关。 事实上,如果单独运行计数查询需要10分钟,并且有8个从站,则可以将时间缩短到几分钟以内。 也许一个小时来解决这个解决scheme的细节。

当然,由于这个分布式求解引入了一些可以删除和插入行的时间,所以你永远不会得到一个非常准确的答案,但是你可以尝试在同一个实例上得到一个分布式行锁,并且得到一个精确的计数在特定的时刻在表中的行。

实际上,这似乎是不可能的,因为你基本上只使用了一个仅限于SQL的解决scheme,我不认为你提供了一个机制来即时跨多个从服务器运行分片和locking的查询。 也许如果你有复制日志文件的控制…这意味着你会为此目的从字面上旋转奴隶,这无疑比在单个机器上运行计数查询慢得多。

所以我有两个2013年便士。

如果插入触发器使用太昂贵,但是可以提供删除触发器 ,并且存在自动递增id ,那么在对整个表进行一次计数并且将计数记为last-countlast-counted-id

那么每天只需要计算id > last-counted-id ,将其添加到last-count ,并存储新的last-counted-id

如果已删除logging的标识<= last-counting-id,则删除触发器将递减最后一个计数。

如果您有一个具有自动递增主键列的典型表结构,其中行永远不会被删除,以下将是确定logging计数的最快方法,并且应该在大多数ANSI兼容的数据库中以类似的方式工作:

 SELECT TOP(1) <primarykeyfield> FROM <table> ORDER BY <primarykeyfield> DESC; 

我使用包含数十亿行数据的MS SQL表进行工作,这些行需要数据的秒级响应时间,包括logging计数。 一个类似的SELECT COUNT(*)将需要几分钟时间进行比较。

我迟到了这个问题,但这里是你可以用MySQL做什么(因为我使用MySQL)。 我在这里分享我的观察:

 1) SELECT COUNT(*) AS TOTAL_ROWS FROM <TABLE_NAME> 

结果
行数: 508534
控制台输出:受影响的行:0发现行数:1警告:0 1个查询的持续时间:0.125秒。
需要一段时间的行数很多,但行数非常精确。

 2) SHOW TABLE STATUS or SHOW TABLE STATUS WHERE NAME="<TABLE_NAME>" 

结果
行数: 511235
控制台输出:受影响的行:0find行:1警告:0 1查询的持续时间:0.250秒摘要:行计数不准确。

 3) SELECT * FROM information_schema.tables WHERE table_schema = DATABASE(); 

结果
行数: 507806
控制台输出:受影响的行:0发现行数:48警告:0 1查询的持续时间:1.701秒。
行数不准确。

我不是MySQL或数据库专家,但是我发现对于非常大的表格,可以使用选项2或3,并获得有多less行存在的“公平想法”。

我需要得到这些行数在UI上显示一些统计数据。 通过上面的查询,我知道总共行数超过50万,所以我想出了“超过50万行”的统计数据,没有显示确切的行数。

也许我没有真正回答OP的问题,但我分享了我在需要这种统计的情况下所做的。 在我的情况下,显示大致的行是可以接受的,所以上述为我工作。

从sysindexes中select行,其中id = Object_ID('TableName')和indid <2

把一个索引放在一些列上。 这应该允许优化器执行索引块的全面扫描,而不是完整扫描表。 这会降低您的IO成本。 看看前后的执行计划。 然后双向测量挂钟时间。

如果你使用的是Oracle,那么这个(假设表格统计信息被更新):

 select <TABLE_NAME>, num_rows, last_analyzed from user_tables 

last_analyzed将显示统计信息上次收集的时间。

对于Sql服务器试试这个

 SELECT T.name, I.rows AS [ROWCOUNT] FROM sys.tables AS T INNER JOIN sys.sysindexes AS I ON T.object_id = I.id AND I.indid < 2 WHERE T.name = 'Your_Table_Name' ORDER BY I.rows DESC 

也许有点晚,但这可能会帮助别人的MSSQL

;使用RecordCount AS(SELECT ROW_NUMBER()OVER(ORDER BY COLUMN_NAME)AS [RowNumber] FROM TABLE_NAME)SELECT MAX(RowNumber)FROM RecordCount