考虑一个在 PostgreSQL 中实现的投票系统,其中每个用户都可以对“foo”投票赞成或反对。有一个foo
表存储所有的“foo信息”,还有一个votes
表存储user_id
,foo_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
为 null 的情况。我忘了提到投票列上有一个 NOT NULL
约束。
此外,许多人指出第一个更容易阅读。是的,这绝对是真的,如果一位同事写了第二个,除非有性能需要,否则我会气得发狂。尽管如此,问题仍然在于两者的表现。 (从技术上讲,如果第一个查询方式慢,那么编写第二个查询就不算犯罪。)
最佳答案
当然,第一个示例更快、更简单且更易于阅读。甚至在得到 slapped with aquatic creatures 之前就应该很明显了.虽然 sum()
比 count()
稍微贵一点,但更重要的是第二个示例需要两次扫描。
但也有一个实际差异:sum()
可以返回 NULL
,其中 count()
没有。我引用 manual on aggregate functions :
It should be noted that except for count, these functions return a null value when no rows are selected. In particular, sum of no rows returns null, not zero as one might expect,
由于您似乎在性能优化方面存在弱点,这里有一个您可能会喜欢的细节:count(*)
is slightly faster than 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);
首先聚合子查询中的值,然后连接到结果。
关于使用
:
关于sql - sum() 与 count(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14998225/