查询有条件地每个不同的id返回一行

时间:2015-04-11 02:05:18

标签: sql postgresql left-join greatest-n-per-group

我正在制作一个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,upvotedt。由于user1没有upvote 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

由于加入,有两个"重复"查询产生的行。我想优先考虑t > f > null行。所以我想保留1 | t行。

Full script with schema+data.

3 个答案:

答案 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)upvotedPRIMARY 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.