健全性检查:count(*)vs count(pkColumn)

时间:2011-09-14 02:55:49

标签: sql performance postgresql

今天我的老板告诉我使用count(pkColumn)代替count(*),说它会更快。我很确定这不是真的,但我不是一个DBA,而是一个Java开发人员所以我想检查一下。

我运行了一些查询计划:

EXPLAIN SELECT COUNT(*) FROM "Foo";
EXPLAIN SELECT COUNT("FooUID") FROM "Foo";
EXPLAIN SELECT COUNT(1) FROM "Foo";

结果:

Aggregate  (cost=1421.01..1421.02 rows=1 width=0)
  ->  Seq Scan on "Foo"  (cost=0.00..1296.81 rows=49681 width=0)

Aggregate  (cost=1421.01..1421.02 rows=1 width=16)
  ->  Seq Scan on "Foo"  (cost=0.00..1296.81 rows=49681 width=16)

Aggregate  (cost=1421.01..1421.02 rows=1 width=0)
  ->  Seq Scan on "Foo"  (cost=0.00..1296.81 rows=49681 width=0)

他们似乎是一样的。有人可以确认我对此是正确的吗?我意识到这已经针对其他语言进行了讨论,但PostgreSQL按照我的理解处理COUNT的方式不同,而且我找不到PostgreSQL等价的问题。

请注意,"FooUID"永远不会是NULL

编辑:使用版本9.0.4

3 个答案:

答案 0 :(得分:6)

你的老板被误导(充其量)。

是的,PostgreSql(至少是我使用的那些,最多8.1)确实稍微区别对待count,因为它必须转到实际的表来获取一些信息,而不仅仅是单独使用索引(使用MVCC,它在表中存储“软”删除信息,因此必须检索每一行以查看它是否已被删除但尚未从索引中删除。

但无论您使用*还是pk_column进行计数,都必须这样做。使用*的优点是DBMS可以自由选择计数的任何非空列,这可能会导致索引处理的磁盘I / O减少。

如果你的老板担心表现,你可以使用一些可能的“伎俩”。

首先,如果您只对行的存在感兴趣,而不是,则可以使用以下更改代码:

select count(*) into myCount from myTable where myCondition;
if myCount > 0 then
    do something
end if;

成:

if exists ( select * from myTable where myCondition ) then
    do something
end if;

其次,您可以使用insertdelete触发器,这些触发器在单独的表中维护两个计数器,用于添加行和删除行。然后,您可以通过简单地从另一个中减去一个来计算行数。使用两个独立的计数器可以防止对插入/删除操作进行序列化,但它仍然是一个巨大的问题,对于大量案例来说可能是不必要的。

您还必须定期缩减计数器(例如,将1000 inserts + 200 deletes修改为800 inserts and 0 deletes)。

第三,您可以使用以下方法获取行计数的估计值(基于最后一次分析操作):

select reltuples from pg_class where relname = 'myTable';

答案 1 :(得分:1)

理论上,count(column)可能比count(*)更快,因为当SQL解析器解析*时,它必须在目录中查找列的名称是什么,这是什么*代表。使用count(column)可能会避免查找此目录。

与其导致的可读性降低相比,这种“优势”是如此微不足道,并且拒绝数据库通过某种其他机制优化您的意图的机会(例如,计算索引列,从而进行仅索引搜索)。 p>

使用count(*)

答案 2 :(得分:1)

在PostgreSQL中:

  • count(*)等于count(PK)

    正如Bohemian所说,将*翻译成字段会有一点延迟;然而,将PK合法化作为有效字段也存在延迟。无论如何,它们的性能都是微不足道的。


补充阅读:


PostgreSQL Docs

  •   

    注意:习惯于使用其他SQL数据库管理系统的用户可能会对计数聚合在应用于整个表时的性能感到失望。像:

    这样的查询      

    SELECT count(*) FROM sometable;

         PostgreSQL将使用整个表的顺序扫描执行