有条件地查询 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,upvotedt。因为 user1 没有给 post2 投票,所以 upvotedf。 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 行。

Full script with schema+data.

你应该可以用 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;

所有其他帖子都没有被指定用户点赞。看来我们不必明确列出这些。

SQL Fiddle.