多个连接和求和的问题

Problems with multiple joins and a sum

如果有以下三个PostgreSQL tables:

Post table:

postid | title | author | created

投票table:

postid | username | vote

如果用户投票 post 赞成,则投票等于 1,如果用户未投票则投票等于 0,如果用户投票 post 反对则投票等于 -1。

评论table:

commentID | parentID | postID | content | author | created

如果评论不是回复,parentID 为 null。

我现在想收到每个 post 的标题、作者、创建日期、所有投票的总和以及 当前登录用户的投票和评论数。 我已经对用户的投票有问题并询问 并且有人帮助我得到以下查询:

SELECT post.postID as postID, post.title as title, post.author as author,
       post.created as created,
       COALESCE(sum(votes.vote), 0) as voteCount,
       COALESCE(sum(votes.vote) FILTER (WHERE votes.username = :username), 0) as userVote
       FROM post 
       LEFT JOIN votes ON post.postID = votes.postID 
       GROUP BY post.postID 
       ORDER BY voteCount DESC

现在我尝试了另一个 LEFT JOIN 来获取这样的评论数:

COUNT(DISTINCT comments) FILTER (WHERE comments.parentID IS NULL) as numComments
LEFT JOIN comments on post.postID = comments.postID

然而,虽然评论数量有效,但每个 post 的投票数量是错误的,因为 由于另一个连接,这些行似乎多次出现,产生了错误的总和,我在想出解决这个问题的方法时遇到了一些麻烦。

我已经尝试将评论数作为子查询获取,这样它就独立于 未成功的投票数。 任何进一步的帮助将不胜感激! :-)

分别计算值。 joins 导致笛卡尔积。这是相关子查询或横向连接帮助的地方:

SELECT p.*, v.*, c.*
FROM post p CROSS JOIN LATERAL
     (SELECT SUM(v.vote) as voteCount,
             SUM(v.vote) FILTER (WHERE v.username = :username), 0) as userVote
      FROM votes v
      WHERE p.postID = v.postID 
     ) v CROSS JOIN LATERAL
     (SELECT SUM(c.vote) as commentCount,
             SUM(c.vote) FILTER (WHERE c.username = :username), 0) as userVote
      FROM comments c
      WHERE p.postID = c.postID 
     ) c
ORDER BY voteCount DESC;

您通常会在加入之前在子查询中 pre-aggregate,如下所示:

SELECT p.*
    COALESCE(v.voteCount, 0) as voteCount,
    COALESCE(v.userVote, 0) as userVote,
    COALESCE(c.numComments, 0) as numComments
FROM post p
LEFT JOIN (
    SELECT postID, 
        SUM(vote) as voteCount, 
        SUM(vote) FILTER (WHERE username = :username) userVote
    FROM votes 
    GROUP BY postID
) v ON v.postID = p.postID 
LEFT JOIN (
    SELECT postID, count(*) numComments
    FROM comments
    WHERE parentID IS NULL
    GROUP BY postID
) c ON c.postID = p.postID
ORDER BY voteCount DESC