18

我有一个页面,我有4个标签显示基于不同表格的4个不同的报告。数(*)真的很贵吗?

我使用select count(*) from <table>查询获得每个表的行数,并显示选项卡上每个表中可用的行数。因此,每次页面回发都会导致执行5个count(*)查询(4个获取计数和1个分页)以及1个获取报告内容的查询。

现在我的问题是:count(*)查询真的很贵 - 我应该在页面的视图状态中保持行计数(至少是那些显示在标签上的),而不是多次查询?

COUNT(*)查询的代价是多少?

+0

您正在使用哪个数据库? (和哪个版本?) – penguat 2010-04-27 10:36:49

+0

sql server 2005 – 2010-04-27 10:40:47

+1

在实际负载下你的页面加载速度是否太慢?如果不是,那么计数不是“太贵”,所以你不应该担心。 – 2010-04-27 10:40:57

回答

7

您需要附加SQL Profileran app level profiler like L2SProf看看之前在你的背景下,真正的查询费用:

  • 猜测的问题是什么,并试图确定一个潜在的解决方案可能带来的益处

  • 允许别人在da interwebs上为你猜测 - 有很多没有引用的错误信息,包括在这个帖子中(但不是在这篇文章中:P)

当你这样做,这将是明确的,最好的方法是什么 - 即SELECT COUNT是否支配的东西与否,等等

,并具有做到这一点,您就可以知道是否您选择做的任何更改都会产生积极或消极的影响。

+0

感谢您的接受!谨慎地简单告诉我们你最终决定做什么和/或在途中发现? – 2010-04-28 07:53:12

8

一般而言,COUNT(*)成本的成本与满足查询条件的记录数加上准备这些记录所需的时间(取决于底层查询复杂度)成正比。

在处理单个表格的简单情况下,经常会有特定的优化措施来降低此类操作的成本。例如,从MySQL中的单个MyISAM表中执行COUNT(*)而没有WHERE条件 - 这是瞬时的,因为它存储在元数据中。

例如,让我们考虑两个查询:

SELECT COUNT(*) 
FROM largeTableA a 

由于每个记录满足查询时,COUNT(*)成本是成正比的表中的记录数(即,正比于它返回什么)(假设它需要访问的行和到位心不是一个具体的优化处理它)

SELECT COUNT(*) 
FROM largeTableA a 
JOIN largeTableB b 
ON  a.id = b.id 

在这种情况下,发动机将最有可能使用HASH JOIN和执行计划将是这样的:

  1. 建立在表
  2. 的小哈希表扫描较大的表,在哈希表
  3. 查找每个记录数的比赛,因为他们去。

在这种情况下,COUNT(*)开销(步骤3)可以忽略不计以及查询时间通过将步骤1和2被完全定义,正在建设的哈希表和寻找它。对于这样的查询,时间将是O(a + b):它并不真正依赖于匹配的数量。

但是,如果有两个a.idb.id索引,MERGE JOIN可以被选择和COUNT(*)时间将正比于匹配的数量再次,由于一个索引查找每个匹配之后将被执行。

+0

当然是指“COUNT(\ *)成本与数量或记录成正比,加上联系数据库所需的时间”。这并不意味着COUNT(*)操作无法与记录总数成正比,是吗? – Armand 2010-04-27 10:55:47

+0

@Alison:查看帖子更新。 – Quassnoi 2010-04-27 11:19:39

+0

我的评论现在没有多大意义 - 这是对先前评论的回复。 – Armand 2010-04-27 13:27:06

0

COUNT(*)可能会特别昂贵,因为它可能会导致加载(和分页)整个表,其中可能只需要对主键进行计数(在某些实现中它已被优化)。

从它的声音来看,您每次都会导致表加载操作,这很慢,但除非运行明显缓慢或导致某种问题,否则不要优化:过早和不必要的优化可能会导致很大的麻烦!

一个索引主键的计数会快得多,但有了索引的成本可能没有任何好处。

+2

这不适用于SQL Server(T-SQL) - count(*)已经过优化,并且是计算每一行的首选方法。 – 2010-04-27 10:43:45

+0

-1您需要在提供此类权限之前提供参考,并且没有附带条件 – 2010-04-27 10:47:01

+3

参考:http://thehobt.blogspot.com/2008/12/debunking-myth-select-count-vs-select.html – 2010-04-27 10:58:26

0

所有的I/O都很贵,如果你没有它就可以完成任务,你应该。但如果需要的话,我不会担心。

您提到将计数存储在视图状态中,当然这是一个选项,只要该代码的行为在该计数错误时可接受,因为基础记录已消失或已添加到该计数中。

2

正如其他人所说的COUNT(*)总是物理计数行,所以如果你可以做一次并缓存结果,那肯定是可取的。

如果您进行基准测试并确定成本可以忽略不计,则您(当前)没有问题。

如果事实证明,通过使用

SELECT rows FROM sysindexes WHERE id = OBJECT_ID('sometable') AND indid < 2

将返回是,你可以让你的分页“模糊”,如“显示1至大约为30,000 500”太贵了,您的方案一个近似值的行数(其近似值,因为它没有更新,直到CHECKPOINT)。

+0

为了澄清,这种技术只测量表中的近似行 - 它不会在存在where子句或连接的情况下工作(除了琐碎的笛卡尔连接),对吗? – 2012-08-08 19:24:55

+0

是的,它仅适用于简单的快照 – 2012-08-09 09:39:41

0

这取决于您对此表中的数据做什么。如果他们经常改变,并且每次都需要他们,也许你可以做出触发器来填充另一个只包含来自该表格的计数的表格。如果你需要单独显示这些数据,也许你可以只为一个特定的表执行“select count(*)...”。我立即想到了这一点,但我敢肯定,还有其他方法可以加快这一进程。缓存数据,也许? :)

1

如果页面变慢,您可以查看的一件事是尽量减少数据库往返次数。即使你的COUNT(*)查询是O(1),如果你做得足够多,这肯定会减慢速度。

不是一次设置和执行5个单独的查询,而是在单个批处理中运行SELECT语句,并一次处理5个结果。

也就是说,如果你使用ADO.NET,这样做(检查略去了错误;无环/非动态为清楚起见):

string sql = "SELECT COUNT(*) FROM Table1; SELECT COUNT(*) FROM Table2;" 

SqlCommand cmd = new SqlCommand(sql, connection); 
SqlDataReader dr = cmd.ExecuteReader(); 

// Defaults to first result set 
dr.Read(); 
int table1Count = (int)dr[0]; 

// Move to second result set 
dr.NextResult(); 
dr.Read(); 
int table2Count = (int)dr[0]; 

如果您使用的是有些类型的ORM,比如NHibernate,应该有一种方法来启用自动查询批处理。