Search code examples
mysqlgroup-concat

mysql group_concat question for rating slices


I'm creating a mini e-commerce app, where users can rate items they purchase.

The rating is an int from 1 to 5 (representing stars).

A user can only rate an item once.

I have the following tables:

  • user
  • user_rates_item
  • item

From there I would like to make a query that select all items, and for each item I need to know how many of each rating value each product has (i.e., how many 5 stars, how many 4 stars, ..., how many 1 stars).

So far, I've got this (complex) query, which works:

select i.id,
       i.label,
       t1.rating,
       t1.nb_ratings


from lks_item i


         inner join
     (
         select item_id,
                rating,
                count(*) as nb_ratings
         from lks_user_rates_item h
         group by h.rating, h.item_id
     ) as t1 on i.id = t1.item_id


where i.item_type = '1'
  and i.status = '1'


order by i.id asc, t1.rating asc


Here is an excerpt of what I get:

+--+--------+------+----------+
|id|label   |rating|nb_ratings|
+--+--------+------+----------+
|21|item1   |1     |2         |
|21|item1   |3     |1         |
|21|item1   |4     |1         |
|21|item1   |5     |1         |
|22|item2   |1     |3         |
|22|item2   |2     |4         |
|22|item2   |3     |3         |
|22|item2   |5     |4         |
|23|item3   |1     |2         |
+--+--------+------+----------+

Although this gets the job done, it returns one row per rating value. So for instance the item #21 has 4 rows (because it has 4 different rating values), and item #22 get 4 rows too.

I'm curious if there is a way to have only one row per item, with the help of the mysql GROUP_CONCAT function.

In other words, is it possible to have a query that get something more like this ?

+--+--------+-------------------+
|id|label   |nb_ratings         |
+--+--------+-------------------+
|21|item1   |1:2, 3:1, 4:1, 5:1 |
|22|item2   |1:3, 2:4, 3:3, 5:4 |
|23|item3   |1:2                |
+--+--------+-------------------+


Solution

  • In your query you can group by i.id, i.label and use group_concat():

    select i.id, i.label,
           group_concat(concat(t.rating, ':', t.nb_ratings) order by t.rating separator ', ') as nb_ratings       
    from lks_item i
    inner join (
      select item_id,
             rating,
             count(*) as nb_ratings
      from lks_user_rates_item
      group by rating, item_id
    ) as t on i.id = t.item_id
    where i.item_type = '1' and i.status = '1'
    group by i.id, i.label
    order by i.id