查询:
SELECT COUNT(*) as count_all,
posts.id as post_id
FROM posts
INNER JOIN votes ON votes.post_id = posts.id
GROUP BY posts.id;
在Postgresql中返回n
条记录:
count_all | post_id
-----------+---------
1 | 6
3 | 4
3 | 5
3 | 1
1 | 9
1 | 10
(6 rows)
我只想检索返回的记录数:6
。
我使用子查询来实现我想要的,但这似乎不是最佳的:
SELECT COUNT(*) FROM (
SELECT COUNT(*) as count_all, posts.id as post_id
FROM posts
INNER JOIN votes ON votes.post_id = posts.id
GROUP BY posts.id
) as x;
如何在PostgreSQL中获得此上下文中的记录数?
答案 0 :(得分:53)
我认为你只需要COUNT(DISTINCT post_id) FROM votes
。
请参阅http://www.postgresql.org/docs/current/static/sql-expressions.html中的“4.2.7。聚合表达式”部分。
编辑:根据欧文的评论纠正了我粗心的错误。答案 1 :(得分:36)
还有EXISTS
:
SELECT count(*) AS post_ct
FROM posts p
WHERE EXISTS (SELECT 1 FROM votes v WHERE v.post_id = p.id);
其中,在PostgreSQL中,n
一侧有多个条目,就像你无疑一样,通常比<{3}}更快:
SELECT count(DISTINCT p.id) AS post_ct
FROM posts p
JOIN votes v ON v.post_id = p.id;
votes
中每个帖子的行数越多,效果差异就越大。只需使用count(DISTINCT x)
尝试。
count(DISTINCT x)
将收集所有行,对它们进行排序或散列,然后仅考虑每个相同集合的第一行。 EXISTS
只扫描votes
(或者,最好是post_id
上的索引),直到找到第一个匹配项。
如果每个post_
id都保证存在于表posts
中(例如,通过外键约束),则此简短形式相当于较长的形式:
SELECT count(DISTINCT post_id) AS post_ct
FROM votes;
这实际上可能比使用EXISTS
的第一个查询更快,每个帖子没有或几个条目。
您的查询也以更简单的形式工作:
SELECT count(*) AS post_ct
FROM (
SELECT 1
FROM posts
JOIN votes ON votes.post_id = posts.id
GROUP BY posts.id
) x;
为了验证我的声明,我在资源有限的测试服务器上运行了基准测试。所有这些都在一个单独的模式中:
假冒典型的投票/投票情况:
CREATE SCHEMA y;
SET search_path = y;
CREATE TABLE posts (
id int PRIMARY KEY -- I don't use "id" as column name
, post text);
INSERT INTO posts
SELECT g, repeat(chr(g%100 + 32), (random()* 500)::int) -- random text
FROM generate_series(1,10000) g;
DELETE FROM posts WHERE random() > 0.9; -- create ~10 % dead tuples
CREATE TABLE votes (
vote_id serial PRIMARY KEY
, post_id int REFERENCES posts(id)
, up_down bool
);
INSERT INTO votes (post_id, up_down)
SELECT g.*
FROM (
SELECT ((random()* 21)^3)::int + 1111 AS post_id -- uneven vote distribution
, random()::int::bool AS up_down
FROM generate_series(1,70000)
) g
JOIN posts p ON p.id = g.post_id;
以下所有查询都返回了相同的结果(9107个帖子中有8093个投票)
我在 Postgres 9.1.4 上使用EXPLAIN ANALYZE
(最好的五个)进行了4次测试,并对三个查询中的每一个进行了测试,并附加了总运行时间。
原样。
After ..
ANALYZE posts;
ANALYZE votes;
After ..
CREATE INDEX foo on votes(post_id);
After ..
VACUUM FULL ANALYZE posts;
CLUSTER votes using foo;
count(*) ... WHERE EXISTS
count(DISTINCT x)
- 带连接的长格式count(DISTINCT x)
- 没有加入的简短形式有问题的原始查询的最佳时间:
简化版:
@worldplasser的CTE查询使用与长格式相同的计划(对帖子进行索引扫描,对投票进行索引扫描,合并连接)以及CTE的一点开销。最佳时间:
EXPLAIN ANALYZE
可以更改每个查询的结果。
DROP SCHEMA y CASCADE; -- clean up
相关的,更详细的Postgres 9.5基准测试(实际上检索不同的行,而不只是计算):
答案 2 :(得分:3)
使用OVER()
和LIMIT 1
:
SELECT COUNT(1) OVER()
FROM posts
INNER JOIN votes ON votes.post_id = posts.id
GROUP BY posts.id
LIMIT 1;
答案 3 :(得分:2)
WITH uniq AS (
SELECT DISTINCT posts.id as post_id
FROM posts
JOIN votes ON votes.post_id = posts.id
-- GROUP BY not needed anymore
-- GROUP BY posts.id
)
SELECT COUNT(*)
FROM uniq;