我正在制作一个Reddit克隆,并且我在查看我的帖子列表时遇到问题,给定一个已登录的用户,显示是否登录用户为每个帖子提供了帖子。我做了一个小例子来简化事情。
我尝试每个不同的post_id
只返回一行,但将upvoted
列的优先级设置为t > f > null
。
对于此示例数据:
> select * from post;
id
----
1
2
3
> select * from users;
id
----
1
2
> select * from upvoted;
user_id | post_id
---------+---------
1 | 1
2 | 1
如果给我user_id = 1
,我希望我的查询返回:
postid | user_upvoted
--------+--------------
1 | t
2 | f
3 | f
由于user1 upvoted post1,upvoted
为t
。由于user1没有upvote post2,upvoted
为f
。对于post3也一样。
CREATE TABLE IF NOT EXISTS post (
id bigserial,
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS users (
id serial,
PRIMARY KEY (id)
);
CREATE TABLE IF NOT EXISTS upvoted (
user_id integer
REFERENCES users(id)
ON DELETE CASCADE ON UPDATE CASCADE,
post_id bigint
REFERENCES post(id)
ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY (user_id, post_id)
);
SELECT post.id as postid,
CASE WHEN user_id=1 THEN true ELSE false END as user_upvoted
FROM post LEFT OUTER JOIN upvoted
ON post_id = post.id;
这给了我:
postid | user_upvoted
--------+--------------
1 | t
1 | f
2 | f
3 | f
由于加入,有两个"重复"查询产生的行。我想优先考虑t > f > null
行。所以我想保留1 | t
行。
答案 0 :(得分:4)
您应该可以使用distinct on
:
SELECT distinct on (p.id) p.id as postid,
(CASE WHEN user_id = 1 THEN true ELSE false END) as upvoted
FROM post p LEFT OUTER JOIN
upvoted u
ON u.post_id = p.id
ORDER BY p.id, upvoted desc;
答案 1 :(得分:1)
exists()
运算符产生一个布尔值:
SELECT p.id
, EXISTS (SELECT * FROM upvoted x
WHERE x.post_id = p.id
AND x.user_id = 1) AS it_was_upvoted_by_user1
FROM post p
;
答案 2 :(得分:1)
由于合并(user_id, post_id)
在upvoted
(PRIMARY KEY
)中定义为唯一,因此更简单:
SELECT p.id AS post_id, u.post_id IS NOT NULL AS user_upvoted
FROM post p
LEFT JOIN upvoted u ON u.post_id = p.id
AND u.user_id = 1;
只需将user_id = 1
添加到连接条件即可。完美地使用索引,应该是最简单和最快的。
您还提到了NULL,但结果中只有两个不同的状态:true
/ false
。
第二个想法,你可能会使一项非常基本的任务复杂化。如果您只对当前用户投票的帖子感兴趣,请改用简单查询:
SELECT post_id FROM upvoted WHERE user_id = 1;
给定用户不会对所有其他帖子进行投票。我们似乎不必明确列出这些内容。