繁体   English   中英

sum()与count()

[英]sum() vs. count()

考虑在PostgreSQL中实现的投票系统,其中每个用户可以在“foo”上向上或向下投票。 有一个foo表,用于存储所有的“富信息”,以及votes存储表user_idfoo_id ,并vote ,其中vote是+1或-1。

要获得每个foo的投票结果,以下查询将起作用:

SELECT sum(vote) FROM votes WHERE foo.foo_id = votes.foo_id;

但是,以下内容也可以正常工作:

(SELECT count(vote) FROM votes 
 WHERE foo.foo_id = votes.foo_id 
 AND votes.vote = 1)
- (SELECT count(vote) FROM votes 
   WHERE foo.foo_id = votes.foo_id 
   AND votes.vote = (-1))

我目前在votes.foo_id上有一个索引。

哪种方法更有效? (换句话说,哪个会运行得更快?)我对PostgreSQL特定的答案和一般的SQL答案感兴趣。

编辑

很多答案都考虑到vote为空的情况。 我忘了提到投票列上有一个NOT NULL约束。

此外,许多人指出,第一个更容易阅读。 是的,这绝对是真的,如果一位同事写了第二篇,我会愤怒地爆发,除非有表演的必要性。 从来没有,问题仍然在于两者的表现。 (从技术上来说,如果第一个查询方法要慢,它不会是这种罪行写入第二个查询。)

当然,第一个例子更快,更简单,更容易阅读。 甚至在被水生生物拍打之前应该是显而易见的。 虽然sum()count()略贵,但更重要的是,第二个例子需要两次扫描。

但是也有一个实际的区别sum()可以返回NULL ,而count()则不会。 我引用了关于聚合函数手册

应该注意,除了count之外,这些函数在没有选择行时返回空值。 特别是,没有行的总和返回null,而不是像人们预期的那样为零,

由于您似乎在性能优化方面存在弱点,因此这里有一个您可能会喜欢的细节: count(*)略快于count(vote) 如果vote为NOT NULL则仅等效。 使用EXPLAIN ANALYZE测试性能。

仔细检查

这两个查询都是语法上的废话,独自站立。 只有从较大查询的SELECT列表中复制它们才有意义:

SELECT *, (SELECT sum(vote) FROM votes WHERE votes.foo_id = foo.foo_id)
FROM   foo;

这里重要的一点是相关子查询 - 如果您只在查询中阅读一小部分 votes ,这可能没问题。 我们会看到其他WHERE条件,您应该有匹配的索引。

在Postgres 9.3或更高版本中,替代的,更清洁,100%等效的解决方案将使用LEFT JOIN LATERAL ... ON true

SELECT *
FROM   foo f
LEFT   JOIN LATERAL (
   SELECT sum(vote) FROM votes WHERE foo_id = f.foo_id
   ) v ON true;

通常类似的表现。 细节:

但是 ,在从表格votes读取大部分或全部内容时 ,这将(更快)更快:

SELECT f.*, v.score
FROM   foo f
JOIN   (
   SELECT foo_id, sum(vote) AS score
   FROM   votes
   GROUP  BY 1
   ) v USING (foo_id);

首先在子查询中聚合值,然后加入到结果中。
关于USING

第一个会更快。 您可以通过简单的方式尝试。

生成一些数据:

CREATE TABLE votes(foo_id integer, vote integer);
-- Insert 1000000 rows into 100 foos (1 to 100)
INSERT INTO votes SELECT round(random()*99)+1, CASE round(random()) WHEN 0 THEN -1 ELSE 1 END FROM generate_series(1, 1000000);
CREATE INDEX idx_votes_id ON votes (foo_id);

检查两个

EXPLAIN ANALYZE SELECT SUM(vote) FROM votes WHERE foo_id = 5;
EXPLAIN ANALYZE SELECT (SELECT COUNT(*) AS count FROM votes WHERE foo_id=5 AND vote=1) - (SELECT COUNT(*)*-1 AS count FROM votes WHERE foo_id=5 AND vote=-1);

但事实是,它们并不等同,为了确保第一个作为第二个,你需要对待null案例:

SELECT COALESCE(SUM(vote), 0) FROM votes WHERE foo_id = 5;

还有一件事。 如果您使用的是PostgreSQL 9.2,则可以使用其中的两列创建索引,这样您就有可能使用仅索引扫描:

CREATE INDEX idx_votes_id ON votes (foo_id, vote);

但! 在某些情况下,这个索引可能是最差的,所以你应该尝试使用两个并运行EXPLAIN ANALYZE以查看哪个是最好的,或者甚至创建两个并检查哪个PostgreSQL使用最多(并排除另一个)。

我希望第一个查询能够更快地工作,因为这是一个单一的查询,并且它更具可读性(如果你不得不在一段时间之后再回到这个问题,那就很方便了)。

第二个查询包含两个查询。 您只能获得一个结果,就像它是一个查询一样。

也就是说,为了绝对确定哪些更适合你,我会用两个表填充大量的伪数据并检查查询执行时间。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM