按 PostgreSQL 中多个内部连接的条件计数
Count by condition of multiple inner joins in PostgreSQL
我有以下 tables:
User
UserPostView
Post
UserPostView
是一个连接 table,其中包含有关 User
在查看 post.
后是否赞成、反对或通过的附加信息
Post
有一列 postable_type
指示 post 的类型(TextPost
、ImagePost
等)。
我想计算按 postable_type
分组的每个用户的赞成票、反对票和通过计数。
我当前的查询非常慢,我很确定它可以轻松优化。
SELECT
U.id,
count((UP.postable_type = 'text_post' AND UPV.passed = true) OR NULL) as text_posts_pass_count,
count((UP.postable_type = 'text_post' AND UPV.upvote = true) OR NULL) as text_posts_upvote_count,
count((UP.postable_type = 'text_post' AND UPV.downvote = true) OR NULL) as text_posts_downvote_count,
count((UP.postable_type = 'image_post' AND UPV.passed = true) OR NULL) as image_posts_pass_count,
count((UP.postable_type = 'image_post' AND UPV.upvote = true) OR NULL) as image_posts_upvote_count,
count((UP.postable_type = 'image_post' AND UPV.downvote = true) OR NULL) as image_posts_downvote_count
FROM
users U
INNER JOIN(
SELECT
user_id,
post_id,
passed,
upvoted,
downvoted
FROM
user_post_views
) UPV on U.id :: TEXT = UPV.user_id :: TEXT
INNER JOIN(
SELECT
id,
postable_type
FROM
posts
) UP on UPV.post_id :: TEXT = UP.id :: TEXT
GROUP BY
U.id
不要对联接进行类型转换!我想你只需要:
SELECT UPV.user_id,
COUNT(*) FILTER (WHERE p.postable_type = 'text_post' AND upv.passed) as text_posts_pass_count,
COUNT(*) FILTER (WHERE p.postable_type = 'text_post' AND upv.upvote) as text_posts_upvote_count,
COUNT(*) FILTER (WHERE p.postable_type = 'text_post' AND upv.downvote ) as text_posts_downvote_count,
COUNT(*) FILTER (WHERE p.postable_type = 'image_post' AND upv.passed) as image_posts_pass_count,
COUNT(*) FILTER (WHERE p.postable_type = 'image_post' AND upv.upvote) as image_posts_upvote_count,
COUNT(*) FILTER (WHERE p.postable_type = 'image_post' AND upv.downvote) as image_posts_downvote_count
FROM user_post_views upv JOIN
posts p
ON upv.post_id = p.id
GROUP BY upv.user_id;
变化:
- 不要对联接进行类型转换!这肯定会阻碍优化器。
users
table好像没必要
- 不需要子查询。
FILTER
比条件聚合稍快。更重要的是,意图更加明确。
我有以下 tables:
User
UserPostView
Post
UserPostView
是一个连接 table,其中包含有关 User
在查看 post.
Post
有一列 postable_type
指示 post 的类型(TextPost
、ImagePost
等)。
我想计算按 postable_type
分组的每个用户的赞成票、反对票和通过计数。
我当前的查询非常慢,我很确定它可以轻松优化。
SELECT
U.id,
count((UP.postable_type = 'text_post' AND UPV.passed = true) OR NULL) as text_posts_pass_count,
count((UP.postable_type = 'text_post' AND UPV.upvote = true) OR NULL) as text_posts_upvote_count,
count((UP.postable_type = 'text_post' AND UPV.downvote = true) OR NULL) as text_posts_downvote_count,
count((UP.postable_type = 'image_post' AND UPV.passed = true) OR NULL) as image_posts_pass_count,
count((UP.postable_type = 'image_post' AND UPV.upvote = true) OR NULL) as image_posts_upvote_count,
count((UP.postable_type = 'image_post' AND UPV.downvote = true) OR NULL) as image_posts_downvote_count
FROM
users U
INNER JOIN(
SELECT
user_id,
post_id,
passed,
upvoted,
downvoted
FROM
user_post_views
) UPV on U.id :: TEXT = UPV.user_id :: TEXT
INNER JOIN(
SELECT
id,
postable_type
FROM
posts
) UP on UPV.post_id :: TEXT = UP.id :: TEXT
GROUP BY
U.id
不要对联接进行类型转换!我想你只需要:
SELECT UPV.user_id,
COUNT(*) FILTER (WHERE p.postable_type = 'text_post' AND upv.passed) as text_posts_pass_count,
COUNT(*) FILTER (WHERE p.postable_type = 'text_post' AND upv.upvote) as text_posts_upvote_count,
COUNT(*) FILTER (WHERE p.postable_type = 'text_post' AND upv.downvote ) as text_posts_downvote_count,
COUNT(*) FILTER (WHERE p.postable_type = 'image_post' AND upv.passed) as image_posts_pass_count,
COUNT(*) FILTER (WHERE p.postable_type = 'image_post' AND upv.upvote) as image_posts_upvote_count,
COUNT(*) FILTER (WHERE p.postable_type = 'image_post' AND upv.downvote) as image_posts_downvote_count
FROM user_post_views upv JOIN
posts p
ON upv.post_id = p.id
GROUP BY upv.user_id;
变化:
- 不要对联接进行类型转换!这肯定会阻碍优化器。
users
table好像没必要- 不需要子查询。
FILTER
比条件聚合稍快。更重要的是,意图更加明确。