我曾经读过一些文章,说当表有很多行和很多列时,SELECT COUNT(*) FROM TABLE_NAME将会很慢。
我有一个可能包含数十亿行的表(它大约有15列)。有没有更好的方法来获得一个表的行数的精确计数?
在回答之前请考虑以下问题:
我正在寻找一个数据库供应商
独立的解决方案。如果是也可以
涵盖MySQL, Oracle, MS SQL Server。
但如果真的没有数据库
供应商独立的解决方案,然后我
会接受不同的解决方案吗
针对不同的数据库供应商。
我不能使用任何外部工具
这样做。我主要是在找一个
基于SQL的解决方案。
我不能规范化我的数据库设计
任何进一步的。它已经在3NF中,而且
很多代码已经写好了
围绕它。
我找到了一篇很好的文章:SQL Server-HOW-TO:快速从martijnh1检索表的准确行数,它很好地概述了每个场景。
我需要在需要根据特定条件提供计数的地方进行扩展,当我计算出这一部分时,我会进一步更新这个答案。
与此同时,以下是文章中的细节:
方法1:
查询:
SELECT COUNT(*) FROM Transactions
评论:
执行全表扫描。在大桌子上慢点。
方法2:
查询:
SELECT CONVERT(bigint, rows)
FROM sysindexes
WHERE id = OBJECT_ID('Transactions')
AND indid < 2
评论:
快速检索行数的方法。取决于统计数据,不准确。
运行DBCC UPDATEUSAGE(数据库)WITH COUNT_ROWS,这对于大型表可能会花费大量时间。
方法3:
查询:
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'))
评论:
SQL管理工作室计算行数的方法(查看表属性、存储、行数)。非常快,但仍然是大概的行数。
方法4:
查询:
SELECT SUM (row_count)
FROM sys.dm_db_partition_stats
WHERE object_id=OBJECT_ID('Transactions')
AND (index_id=0 or index_id=1);
评论:
操作快(虽然不如方法二快),同样重要的是,可靠。
我不认为有一个通用的总是最快的解决方案:一些RDBMS/版本对SELECT COUNT(*)进行了特定的优化,使用更快的选项,而其他版本只是简单的表扫描。对于第二组,您需要访问文档/支持站点,这可能需要编写一些更具体的查询,通常是以某种方式命中索引的查询。
编辑:
Here's a thought that might work, depending on your schema and distribution of data: do you have an indexed column that references an increasing value, a numeric increasing ID, say, or even a timestamp or date? Then, assuming deletes don't happen, it should be possible to store the count up to some recent value (yesterday's date, highest ID value at some recent sample point) and add the count beyond that, which should resolve very quickly in the index. Very dependent on values and indices, of course, but applicable to pretty much any version of any DBMS.
这并不是一个与dbms无关的解决方案,但至少您的客户端代码看不到区别……
创建另一个只有一行和一个整数字段N1的表T,并创建INSERT TRIGGER,只执行:
UPDATE T SET N = N + 1
还可以创建一个DELETE TRIGGER来执行:
UPDATE T SET N = N - 1
一个称职的DBMS将保证2以上操作的原子性,并且N将始终包含准确的行数,然后超级快速地简单地获得:
SELECT N FROM T
虽然触发器是特定于DBMS的,但从T中选择不是,并且您的客户端代码不需要为每个受支持的DBMS更改。
但是,如果表是INSERT或DELETE密集型的,这可能会有一些可伸缩性问题,特别是如果在INSERT/DELETE之后没有立即提交。
1这些名称只是占位符——在生产中使用更有意义的名称。
也就是说,N不能通过读和写N之间的并发事务来改变,只要读和写都是在一条SQL语句中完成的。
我曾经读过一些文章,说当表有很多行和很多列时,SELECT COUNT(*) FROM TABLE_NAME将会很慢。
这取决于数据库。有些方法可以加速计数,例如通过跟踪索引中的行是活的还是死的,从而允许只扫描索引来提取行数。其他的则不是,因此需要访问整个表并逐个计算活动行。对于一张大桌子来说,这两种方式都很慢。
请注意,您通常可以通过使用查询优化工具、表统计信息等提取一个良好的估计。例如,在PostgreSQL的例子中,你可以从你的表中解析explain count(*)的输出,并得到一个相当好的行数估计。这就引出了你的第二个问题。
我有一个可能包含数十亿行的表(它大约有15列)。有没有更好的方法来获得一个表的行数的精确计数?
严重吗?:-)你真的是指一个有数十亿行的表的精确计数吗?你真的确定吗?: -)
如果您真的这样做了,您可以使用触发器跟踪总数,但如果这样做了,请注意并发性和死锁。
有没有更好的方法来获得一个表的行数的精确计数?
简单地回答你的问题,没有。
如果你需要一个独立于DBMS的方法来做这件事,最快的方法总是:
SELECT COUNT(*) FROM TableName
一些DBMS供应商可能有更快的方法,只适用于他们的系统。其中一些选项已经在其他答案中发布了。
COUNT(*)应该由DBMS(至少是任何值得PROD的DB)进行优化,所以不要试图绕过它们的优化。
On a side note:
I am sure many of your other queries also take a long time to finish because of your table size. Any performance concerns should probably be addressed by thinking about your schema design with speed in mind. I realize you said that it is not an option to change but it might turn out that 10+ minute queries aren't an option either. 3rd NF is not always the best approach when you need speed, and sometimes data can be partitioned in several tables if the records don't have to be stored together. Something to think about...