伯爵(*)真的很贵吗?

时间:2010-04-27 10:11:25

标签: asp.net sql-server-2005 performance premature-optimization

我有一个页面,其中有4个标签显示基于不同表格的4种不同报告。

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

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

COUNT(*)查询的费用是多少?

7 个答案:

答案 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,执行计划将是这样的:

  1. 在较小的表
  2. 上构建哈希表
  3. 扫描较大的表,查找哈希表中的每个记录
  4. 随时计算比赛数。
  5. 在这种情况下,COUNT(*)开销(步骤3)可以忽略不计,查询时间将完全由步骤1和2定义,即构建哈希表并进行查找。对于这样的查询,时间将是O(a + b):它实际上并不取决于匹配的数量。

    但是,如果a.idb.id都有索引,则可以选择MERGE JOINCOUNT(*)时间将再次与匹配数成比例,因为每次比赛后都会进行指数搜寻。

答案 1 :(得分:7)

您需要附上SQL Profileran 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(*)...”。我立即想到了这一点,但是还有其他方法可以加快速度,我敢肯定。缓存数据,也许? :)