クエリ:
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でこのコンテキストのレコード数を取得するにはどうすればよいですか?
COUNT(DISTINCT post_id) FROM votes
だけが必要だと思います。
http://www.postgresql.org/docs/current/static/sql-expressions.html の「4.2.7。集約式」セクションを参照してください。
編集:アーウィンのコメントごとに私の不注意な間違いを修正しました。
また、 EXISTS
:
_SELECT count(*) AS post_ct
FROM posts p
WHERE EXISTS (SELECT FROM votes v WHERE v.post_id = p.id);
_
Postgresでは、おそらくn-sideに複数のエントリがあり、おそらくfasterよりも count(DISTINCT post_id)
:
_SELECT count(DISTINCT p.id) AS post_ct
FROM posts p
JOIN votes v ON v.post_id = p.id;
_
votes
に投稿ごとの行が多いほど、パフォーマンスの差は大きくなります。 _EXPLAIN ANALYZE
_ でテストします。
count(DISTINCT post_id)
はall行を読み取り、それらをソートまたはハッシュし、同一セットごとに最初の1つだけを考慮する必要があります。 EXISTS
は、最初の一致が見つかるまでvotes
(または、できれば_post_id
_のインデックス)のみをスキャンします。
If votes
内のすべての_post_id
_がテーブルposts
(外部キー制約で実施される参照整合性)に存在することが保証されます。この短い形式は長い形式と同等です:
_SELECT count(DISTINCT post_id) AS post_ct
FROM votes;
_
実際には、投稿ごとにnoまたは少数のエントリを指定したEXISTS
クエリよりも高速です。
あなたが持っていたクエリもより簡単な形で動作します:
_SELECT count(*) AS post_ct
FROM (
SELECT FROM posts
JOIN votes ON votes.post_id = posts.id
GROUP BY posts.id
) sub;
_
自分の主張を検証するために、リソースが限られているテストサーバーでベンチマークを実行しました。すべて別のスキーマ内:
典型的な投稿/投票状況を偽る:
_CREATE SCHEMA y;
SET search_path = y;
CREATE TABLE posts (
id int PRIMARY KEY
, 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 distribution
, random()::int::bool AS up_down
FROM generate_series(1,70000)
) g
JOIN posts p ON p.id = g.post_id;
_
以下のクエリはすべて同じ結果を返しました(9107の投稿のうち8093の投稿に投票がありました)。EXPLAIN ANALYZE
_ antを使用して4つのテストを実行し、3つのクエリのそれぞれで Postgres 9.1.4 で5つのベストを取り、結果のランタイムの合計。
そのまま。
後..
_ANALYZE posts;
ANALYZE votes;
_
後..
_CREATE INDEX foo on votes(post_id);
_
後..
_VACUUM FULL ANALYZE posts;
CLUSTER votes using foo;
_
count(*) ... WHERE EXISTS
count(DISTINCT x)
-結合付きの長い形式count(DISTINCT x)
-結合なしの短い形式問題の元のクエリの最適な時間:
簡易バージョンの場合:
@ wildplasserのCTE を使用したクエリは、長い形式(投稿のインデックススキャン、投票のインデックススキャン、マージ結合)と同じプランに加えて、CTEの少しのオーバーヘッドを使用します。ベストタイム:
今後のPostgreSQL 9.2 でのインデックスのみのスキャンは、これらの各クエリの結果を改善できます。ほとんどの場合、EXISTS
の結果です。
関連する、Postgres 9.5のより詳細なベンチマーク(実際にカウントするだけでなく、個別の行を取得する):
OVER()
およびLIMIT 1
:
SELECT COUNT(1) OVER()
FROM posts
INNER JOIN votes ON votes.post_id = posts.id
GROUP BY posts.id
LIMIT 1;
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;