如何使用 LEFT JOIN 并根据计数将结果限制为一行

How to use LEFT JOIN and limit result to one row based on a count

假设我有 3 tables:

Table 'post'

id title
0 titleA
1 titleB
2 titleC

Table 'comment'

id id_post text
0 1 blaa
1 3 blbb
2 5 blcc

Table 'like'

id id_comment vote
0 1 +1
1 5 -1
2 5 +1

我需要获得 post 条评论最多的列表。

查询应该对每个post的每个评论的table“喜欢”(非真实姓名)的投票列(+1,-1 ...)求和,保留higest 加到对应的post.

例如:

post_id post_title comment_id comment_text like_vote
0 titleA 12 blaaaa 51
1 titleB 25 blabbb 98
2 titleC 63 blaccc 14
$statement = $this->pdo->prepare('SELECT SQL_NO_CACHE p.id AS post_id, p.title AS post_title, c.id AS comment_id, c.text AS comment_text, IFNULL(like.like_vote, 0) AS like_vote FROM post p
    LEFT JOIN  (SELECT * FROM comment) c ON p.id = c.id_post
    LEFT JOIN  (SELECT id_comment, IFNULL(SUM(vote), 0) AS like_vote, FROM like GROUP BY id_comment ORDER BY like_vote DESC) like ON c.id = like.id_comment 
    ORDER BY p.id ASC
    ');

我得到的结果是,当 post 有 2 条评论时,我得到 2 post 每条评论和计数如下:

post_id: 0, comment_id: 1, like_vote: 5
post_id: 0, comment_id: 2, like_vote: 7
post_id: 1, comment_id: 3, like_vote: 10
post_id: 1, comment_id: 4, like_vote: 3
...

每条评论都得到正确的赞数,但我不知道如何只保留最高评论并避免多行 post 具有相同的 id,像这样:

post_id: 0, comment_id: 2, like_vote: 7
post_id: 1, comment_id: 3, like_vote: 10

如果你能帮助我构建查询那就太好了,我只是找不到它...

编辑:(table 赞的名称只是一个例子,以保持清楚)

你可以写这个查询,你会得到预期的结果。

我只是将 table like 重命名为 comment_like 因为 like 是预定义的词,所以使用预定义的词不是好的做法。

    SELECT p.id AS post_id, p.title AS post_title, c.id AS comment_id, c.text AS comment_text, IFNULL(comment_like.like_vote, 0) AS like_vote FROM post p 
LEFT JOIN comment c ON p.id = c.id_post 
LEFT JOIN (SELECT id_comment, IFNULL(SUM(vote), 0) AS like_vote FROM comment_like GROUP BY id_comment ORDER BY like_vote DESC) comment_like ON c.id = comment_like.id_comment ORDER BY p.id ASC;

一种方法是使用 CTE。使用 ROW_NUMBER() OVER(...) 按 post 和最大票数对结果进行排序和排名。然后拿最大的那个,即where votes_rank = 1

post_id comment_id like_votes votes_rank
0 1 5 1
0 2 7 2
1 4 3 1
1 3 10 2

SQL (MySQL 8.x)

注意 - 使用 table 名称 comment_likes 以避免必须转义关键字 like

WITH cte AS (
   SELECT p.id AS post_id
          , c.id AS comment_id
          , l.like_votes
          , ROW_NUMBER() OVER(
              PARTITION BY p.id
              ORDER BY l.like_votes DESC
          ) AS votes_rank
   FROM   post p 
             LEFT JOIN comment c ON c.id_post = p.id
             LEFT JOIN (
                 SELECT id_comment, SUM(vote) AS like_votes
                 FROM   comment_likes
                 GROUP BY id_comment
             )
             l ON l.id_comment = c.id
)
SELECT * 
FROM   cte
WHERE  votes_rank = 1
AND    like_votes IS NOT NULL
ORDER BY post_id, like_votes

结果:

post_id comment_id like_votes votes_rank
0 2 7 1
1 3 10 1

db<>fiddle here


更新 2022-04-01

MySQL 5.x 不支持 window 函数,因此您必须求助于子查询。这样的事情应该会产生类似的结果

 SELECT   p.id AS post_id
          , ( SELECT  c.id AS comment_id
               FROM   comment c INNER JOIN comment_likes l ON l.id_comment = c.id
               WHERE  c.id_post = p.id
               GROUP BY c.id
               ORDER BY SUM(vote) DESC
               LIMIT 1
           ) AS comment_id
           , ( SELECT SUM(l.vote) 
               FROM   comment c INNER JOIN comment_likes l ON l.id_comment = c.id
               WHERE  p.id = c.id_post
               GROUP BY c.id
               ORDER BY SUM(l.vote) DESC
               LIMIT 1
           ) AS total_like_votes
 FROM   post p 
 HAVING total_like_votes IS NOT NULL

结果:

post_id comment_id total_like_votes
0 2 7
1 3 10

db<>fiddle here