我曾经读过一些文章,说当表有很多行和很多列时,SELECT COUNT(*) FROM TABLE_NAME将会很慢。
我有一个可能包含数十亿行的表(它大约有15列)。有没有更好的方法来获得一个表的行数的精确计数?
在回答之前请考虑以下问题:
我正在寻找一个数据库供应商
独立的解决方案。如果是也可以
涵盖MySQL, Oracle, MS SQL Server。
但如果真的没有数据库
供应商独立的解决方案,然后我
会接受不同的解决方案吗
针对不同的数据库供应商。
我不能使用任何外部工具
这样做。我主要是在找一个
基于SQL的解决方案。
我不能规范化我的数据库设计
任何进一步的。它已经在3NF中,而且
很多代码已经写好了
围绕它。
我说这个问题有点晚了,但下面是你可以用MySQL做什么(就像我使用MySQL一样)。我在这里分享我的观察:
1) SELECT COUNT(*) AS TOTAL_ROWS FROM <TABLE_NAME>
结果
行数:508534
控制台输出:受影响的行:0发现的行:1警告:0一次查询的持续时间:0.125秒。
对于有大量行的表需要一些时间,但是行数非常精确。
2) SHOW TABLE STATUS or SHOW TABLE STATUS WHERE NAME="<TABLE_NAME>"
结果
行数:511235
控制台输出:受影响的行:0发现的行:1警告:0一次查询的持续时间:0.250秒
总结:行数不准确。
3) SELECT * FROM information_schema.tables WHERE table_schema = DATABASE();
结果
行数:507806
控制台输出:受影响的行:0发现的行:48警告:0查询一次的持续时间:1.701秒。
行数不准确。
我不是MySQL或数据库专家,但我发现对于非常大的表,你可以使用选项2或3,并得到一个“公平的想法”有多少行。
我需要获得这些行数,以便在UI上显示一些统计信息。通过上面的查询,我知道总行数超过了50万,所以我提出了显示“超过50万行”这样的统计信息,但没有显示确切的行数。
也许我没有真正回答OP的问题,但我在分享我在需要这样的统计数据的情况下所做的事情。在我的情况下,显示大致的行是可以接受的,所以上面的工作对我来说。
如果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可以工作,但强烈建议避免在将来的SQL Server版本中使用它,因为它可能在不久的将来被删除。
示例代码摘自:如何快速轻松地获取表行计数
简单的回答是:
数据库供应商独立的解决方案=使用标准= COUNT(*)
有近似的SQL Server解决方案,但不要使用COUNT(*) =超出范围
注:
COUNT(1) = COUNT(*) = COUNT(主键)以防万一
编辑:
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分钟,计数= 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)
2次,都在1秒内,计数= 1,401,659,670
第二个有较少的rows =错误。相同或更多取决于写入(这里的删除是按小时计算的)
这并不是一个与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列)。有没有更好的方法来获得一个表的行数的精确计数?
严重吗?:-)你真的是指一个有数十亿行的表的精确计数吗?你真的确定吗?: -)
如果您真的这样做了,您可以使用触发器跟踪总数,但如果这样做了,请注意并发性和死锁。