我有一个页面,其中有4个标签显示基于不同表格的4种不同报告。
我使用select count(*) from <table>
查询获取每个表的行数,并在选项卡上显示每个表中可用的行数。因此,每个页面回发都会导致执行5个count(*)
个查询(4个用于获取计数,1个用于分页)和1个用于获取报告内容的查询。
现在我的问题是:count(*)
查询真的很贵 - 我应该在页面的视图状态中保留行数(至少显示在选项卡上的那些)而不是多次查询吗?
COUNT(*)查询的费用是多少?
答案 0 :(得分:8)
通常,COUNT(*)
成本的成本与满足查询条件的记录数加上准备这些记录所需的时间(取决于基础查询复杂性)成正比。
在您处理单个表的简单情况下,通常会进行特定的优化以使此类操作变得便宜。例如,在COUNT(*)
中的单个WHERE
表中执行MyISAM
条件MySQL
- 这是瞬时的,因为它存储在元数据中。
例如,让我们考虑两个查询:
SELECT COUNT(*)
FROM largeTableA a
由于每条记录都满足查询,COUNT(*)
成本与表中记录的数量成正比(即与其返回的成比例)(假设它需要访问行并且没有特定的优化到位处理它)
SELECT COUNT(*)
FROM largeTableA a
JOIN largeTableB b
ON a.id = b.id
在这种情况下,引擎最有可能使用HASH JOIN
,执行计划将是这样的:
在这种情况下,COUNT(*)
开销(步骤3)可以忽略不计,查询时间将完全由步骤1和2定义,即构建哈希表并进行查找。对于这样的查询,时间将是O(a + b)
:它实际上并不取决于匹配的数量。
但是,如果a.id
和b.id
都有索引,则可以选择MERGE JOIN
,COUNT(*)
时间将再次与匹配数成比例,因为每次比赛后都会进行指数搜寻。
答案 1 :(得分:7)
您需要附上SQL Profiler或an app level profiler like L2SProf并查看上下文中的实际查询费用:
猜测问题是什么,并试图确定潜在解决方案的可能好处
允许其他人在da interwebs上猜你 - 有很多错误信息没有引用,包括在这个帖子中(但不在这篇文章中:P)
当你这样做时,最清楚的是最好的方法是什么 - 即SELECT COUNT是否支配主导等等。
完成此操作后,您还会知道您选择做出的任何更改是否会产生正面或负面影响。
答案 2 :(得分:2)
正如其他人所说COUNT(*)
总是在物理上对行进行计数,所以如果你能做到一次并缓存结果,那肯定是可取的。
如果您进行基准测试并确定成本可以忽略不计,那么您(目前)没有问题。
如果对你的情况来说太贵了,你可以通过使用
使“显示1到500大约30,000 ”中的分页“模糊” SELECT rows FROM sysindexes WHERE id = OBJECT_ID('sometable') AND indid < 2
将返回近似的行数(其近似值,因为直到CHECKPOINT才更新)。
答案 3 :(得分: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,应该有办法启用自动查询批处理。
答案 4 :(得分:0)
COUNT(*)可能特别昂贵,因为它可能导致加载(和分页)整个表,您可能只需要对主键进行计数(在某些实现中它已经过优化)。
从它的声音来看,你每次都会造成一个表加载操作,这很慢,但除非它运行速度明显缓慢或造成某种问题,否则不要优化:过早和不必要的优化会导致很麻烦!
对索引主键的计数会快得多,但是如果有索引的成本,这可能没有任何好处。
答案 5 :(得分:0)
所有I / O都很昂贵,如果你能在没有它的情况下完成任务,你应该这样做。但如果需要,我不担心。
您提到将计数存储在视图状态中,当然是一个选项,只要当该计数错误时代码的行为是可接受的,因为基础记录已经消失或已被添加到。
答案 6 :(得分:0)
这取决于您对此表中的数据做了什么。如果它们经常变化并且你每次都需要它们,那么也许你可以制作触发器来填充另一个表,该表只包含来自该表的计数。如果您需要单独显示这些数据,也许您只能对一个特定的表执行“select count(*)...”。我立即想到了这一点,但是还有其他方法可以加快速度,我敢肯定。缓存数据,也许? :)