Search code examples
mysqljoingroup-bygroup-concat

MySQL selecting multiple relational tables into one row with group_concat


I have 5 tables: productCategory, products, productOptions, options, optionsGroup. What I'm trying to do is somehow concat and group them into one query to return one row with all information about one product. For example, a certain type of food will have different sizes and different flavors; bottles have different sizes and smells. Here are the tables:

productCategory

categoryId    categoryName
1             food
2             freshener

product

productId    productCategoryRef    productName
1            1                     ....
2            1
3            2
4            2
FK references categoryId

productOptions

categoryRef  optionsRef
1            1
1            3
2            2
2            4
FK categoryRef references productCategory (categoryId)
FK optionsRef references options (optionsId)

options

optionsId    optionsName
1            kg
2            ml
3            Flavor
4            Fragrance

optionsGroup

groupId    groupOptionRef  groupName
1          1               3
2          1               7
3          1               14
4          2               50
5          2               250
6          3               Chicken
7          3               Beef
8          4               Alpine
9          4               Berries
FK groupOptionRef references options (optionId)

So a food bag can be 3kg, 7kg or 14kg and can have two different flavors. How can I get all this information in one row in a readable format? Here's what I tried:

SELECT 
  pc.*, p.*, 
  GROUP_CONCAT(DISTINCT o.optionsName) AS optionName, 
  GROUP_CONCAT(og.groupName) AS options 
FROM productCategories pc 
  JOIN products p ON p.productCategoryId=pc.categoryId 
  JOIN productOptions po ON po.categoryRef=pc.categoryId 
  JOIN options o ON o.optionsId=po.optionsRef 
  JOIN optionsGroup og ON og.groupOptionRef=o.optionsId 
WHERE p.productName=:itemName 
  GROUP BY p.productId

This gives me one row with

....
optionName: 'kg,Flavor',
options: '3,7,14,Chicken,Beef'
....

which is a mess. Is it possible to somehow concat each option together and then post-process it easier without having to guess which part goes to kg/Flavor?

I know it would be easy here but later there will be a review section which will reference this productId which I would like to join as well to this query. Any advice is appreciated, I'm open to a better DB structure if one is needed.


Solution

  • If you don't mind option names being repeated, you can group_concat a concat like this:

    GROUP_CONCAT(CONCAT(o.optionsName, ': ', og.groupName) ORDER BY o.optionsName, og.groupName) AS options 
    

    If you want any more structured than that, you'd have to subquery (grouping by option name, and group_concating it's group names; then grouping without option name in the outer query); but at that point it is definitely worth considering if you should just be handling formatting and grouping the results client side.