Postgresql return 每条评论的回复数

Postgresql return number of replies for each comment

我有一条评论 table 看起来像:

CREATE TABLE comments (
    comment_id INT GENERATED ALWAYS AS IDENTITY,
    user_id VARCHAR(255) NOT NULL,
    username VARCHAR(15) NOT NULL,
    content VARCHAR(255) NOT NULL,
    created_at TIMESTAMP WITHOUT TIME ZONE DEFAULT now(),
    post_id VARCHAR(255) references posts(post_id),
    reply_to INT,
);

对于每条评论,我都在尝试 select 回复的数量。作为回复的评论将有一个 reply_to 列引用 comment_id 回复也是回应。

我正在尝试 select 所有响应 post 的列,但是我还想 select 每个评论的回复数量。

这是查询:

SELECT comment_id, username, content, 
( 
 SELECT COUNT(comments.*) FROM comments 
 JOIN comments comments1 on comments.comment_id = comments1.reply_to
)
as replies FROM comments WHERE post_id = 'uuid';

查询returns:

comment_id username content replies
2 username1 comment content 1
3 username2 comment content 1

问题是 returns 两条评论的回复计数均为 1,即使我只有 1 条记录的 reply_to 列为 3。

为什么您的解决方案不起作用

您在 SELECT 子句中使用的子查询是在内部连接所有评论及其所有回复,然后对结果 table 中的所有结果行进行计数。请注意,您在示例中使用了内部联接(JOIN 是 shorthand 表示 INNER JOIN),因此如果评论没有任何回复,则它不会包含在结果 table 中。所以计数是所有至少有一个回复的评论。

( 
 -- you are counting ***ALL** the comments in your table inner joined onto their replies.
 SELECT COUNT(comments.*) FROM comments 
 JOIN comments comments1 on comments.comment_id = comments1.reply_to
)

但是您希望将计数范围限定为每个评论,以便它只是他们回复的计数,而不是整个 table。

解决方法

假设回复只能深入一层,那么我相信您可以使用横向连接或使用 window 函数的常见 table 表达式。

这里是用横向连接解决它的方法。如果我以后有时间,我会用 window 函数方法更新我的答案。然而,我认为平均而言,横向连接比 window 函数方法更高效,尽管它可能取决于您的数据。

create temporary table comments (
    id serial primary key,
  post_id int,
  reply_to_id int, 
  body varchar
);

-- the body for each comment illustrates the comment hierarchy for a post
-- POST.PARENT_COMMENT.REPLY. E.g. 1.1.1  means this comment is a reply to the first comment for the first post.
insert into comments (post_id, reply_to_id, body) values
(1, null, '1.1'),
(1, 1, '1.1.1'),
(1, 1, '1.1.2'),
(1, null, '1.2'),
(2, null, '2.1'),
(2, 5, '2.1.1');

-- Lets look at the comments with their replies 
select comments.*, replies.* from comments
left outer join comments as replies on comments.id = replies.reply_to_id
order by comments.post_id, replies.reply_to_id ASC;

/*
|             COMMENT               |             REPLY                  |
 id | post_id | reply_to_id | body  | id | post_id | reply_to_id | body
----+---------+-------------+-------+----+---------+-------------+-------
  1 |       1 |             | 1.1   |  3 |       1 |           1 | 1.1.2
  1 |       1 |             | 1.1   |  2 |       1 |           1 | 1.1.1
  3 |       1 |           1 | 1.1.2 |    |         |             |
  2 |       1 |           1 | 1.1.1 |    |         |             |
  4 |       1 |             | 1.2   |    |         |             |
  5 |       2 |             | 2.1   |  6 |       2 |           5 | 2.1.1
  6 |       2 |           5 | 2.1.1 |    |         |             |
*/

--- now lets get the reply count 
select comments.*, r.reply_count from comments
left join lateral (
        select count(replies.id) as reply_count from comments as replies
  where comments.id = replies.reply_to_id
) as r on true
order by comments.post_id ASC;

/*
 id | post_id | reply_to_id | body  | reply_count
----+---------+-------------+-------+-------------
  1 |       1 |             | 1.1   |           2
  2 |       1 |           1 | 1.1.1 |           0
  3 |       1 |           1 | 1.1.2 |           0
  4 |       1 |             | 1.2   |           0
  5 |       2 |             | 2.1   |           1
  6 |       2 |           5 | 2.1.1 |           0
*/