有条件地查询 return 每个不同的 id 只有一行
Query conditionally return only one row per distinct id
我正在制作一个 Reddit 克隆,我在查询我的 post 列表时遇到问题,给定一个登录用户,它显示登录用户是否为 post 投票每个 post。我做了一个小例子让事情更简单。
我正在尝试 return 每个不同 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
我希望我的查询 return:
postid | user_upvoted
--------+--------------
1 | t
2 | f
3 | f
由于 user1 赞成 post1,upvoted
是 t
。因为 user1 没有给 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
由于连接,查询产生了两个 "duplicate" 行。我想优先考虑 t > f > null
的行。所以我想保留 1 | t
行。
你应该可以用 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;
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
;
由于组合 (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;
所有其他帖子都没有被指定用户点赞。看来我们不必明确列出这些。
我正在制作一个 Reddit 克隆,我在查询我的 post 列表时遇到问题,给定一个登录用户,它显示登录用户是否为 post 投票每个 post。我做了一个小例子让事情更简单。
我正在尝试 return 每个不同 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
我希望我的查询 return:
postid | user_upvoted
--------+--------------
1 | t
2 | f
3 | f
由于 user1 赞成 post1,upvoted
是 t
。因为 user1 没有给 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
由于连接,查询产生了两个 "duplicate" 行。我想优先考虑 t > f > null
的行。所以我想保留 1 | t
行。
你应该可以用 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;
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
;
由于组合 (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;
所有其他帖子都没有被指定用户点赞。看来我们不必明确列出这些。