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

Time:04-01

Let's say i have 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

I need to get a list of post with the most liked comment.

The query sould sum the vote column ( 1, -1...) of the table "like" (not the real name) for each comment per post, keep the higest and add it to the corresponding post.

For example:

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
    ');

The result i get is, when a post has 2 comment, i get 2 post with each comment and the count like:

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
...

Each comment get the correct sum of like but i don't know how to keep only the highest comment and avoid multiple row of post with the same id, like this:

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

If you can help my to build the query it would be very nice, i just don't find it...

Edit: (the name of the table like is just a example to keep things clear)

CodePudding user response:

you can write this query and you will get expected result.

I just rename table like to comment_like coz like is predefined word so is not good practice to use predefined word.

    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;

CodePudding user response:

One way is using a CTE. Use ROW_NUMBER() OVER(...) to sort and rank results by post and largest number of votes. Then grab the largest one, i.e. 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:

Note - using table name comment_likes to avoid having to escape keyword 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
          ) 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

Results:

post_id comment_id like_votes votes_rank
0 1 5 1
1 4 3 1

db<>fiddle here

  • Related