Question

I got 3 tables that i need to get data from:

RECIPE - id, name, worktime, date, describtion

TAGS - id, name

INGREDIENT - id, name, created

Now, both tags and ingredient has a many-to-many relation with recipe.

When i try to query my DB to generate a result, i get way too many results.

SELECT * 
FROM (`recipe` r, `tag` t, `ingredient` i) 
JOIN `recipe_has_tag` rt ON `rt`.`tag_t_id` = `t`.`t_id` 
JOIN `ingredient_has_recipe` ir ON `ir`.`ingredient_in_id` = `i`.`in_id` 
WHERE `r`.`r_id` = ir.recipe_r_id 
AND r.r_id = rt.recipe_r_id 
AND r.r_id = ir.recipe_r_id 
AND r.r_id = 1   

I this particular example, i am looking to get 1 recipe, 4 tags and 4 ingredients.

The problem is, instead of returning 4 rows, it mixes the ingredients with the tags, returning 16 results instead of the expected 4.

Right now i end up with 16 results where the each tag has a row with an ingredient, making it 16 results.

Getting something like this (very simplified)

R.name = good dish, i.name = potato, t.name = "potato dish" 
R.name = good dish, i.name = carrot, t.name = "potato dish" 
R.name = good dish, i.name = potato, t.name = "carrot dish" 
R.name = good dish, i.name = carrot, t.name = "carrot dish" 

What i want is rows where each row contains 1 tag and 1 ingredient, like this

R.name = good dish, i.name = potato, t.name = "potato dish" 
R.name = good dish, i.name = carrot, t.name = "carrot dish"

And a fiddle for good measure, showing a one line but with too many results in the tags and ingredient column

http://sqlfiddle.com/#!2/626760/2

Was it helpful?

Solution

Well, as soon as you're trying to retrieve 1 recipe, 4 tags and 4 ingredients, the question is: how do you want it? As you're going to 3 tables, and each has 4 and 4, logical is getting 16 rows: in each row, you'll have one propierty from what you look for.

Other question is if you wanted to get all of them in an only row, in that case, you'll have to group all of them in a field, if you wanted to retrieve it all at the same time:

I guess you have index tables like this:

recipe_has_tag - recipe_r_id, tag_t_id
ingredients_has_recipe - recipe_r_id, ingredient_in_id

So, what I'd do is retrieving all in a single row, concat all the fields with the name of tags and ingredients, and then in PHP explode them or show them directly, depending of what you wanted:

SELECT r.*, 
GROUP_CONCAT( t.name SEPARATOR ', ' ) , GROUP_CONCAT( i.name SEPARATOR ', ')
FROM recipe r

-- Get Tags from recipe
JOIN recipe_has_tag rht ON rht.recipe_r_id  = r.id
JOIN tag t ON rht.tag_t_id = t.id

-- Get Ingredients from recipe
JOIN ingredient_has_recipe ihr ON ihr.recipe_r_id = r.id
JOIN ingredients i ON ihr.ingredient_in_id = i.id

WHERE r.id = 1   
GROUP BY r.id

Other way would be to have different queries according to what you wanted to get, but as long as I don't know what you're going to do with the data, I can't propose anything else.

Take into account, that I'm guessing the name of the index tables and the joins, maybe you'll have to modify the fileds to have the right name

UPDATE

According to your comment, you probably have duplicated values in your tables. The best solution would be check all the values and make a normalized table, but you may do a workaround if you change

-- Change:
GROUP_CONCAT( t.name SEPARATOR ', ' ) , GROUP_CONCAT( i.name SEPARATOR ', ')
-- by 
GROUP_CONCAT( DISTINCT( t.name SEPARATOR ) ', ' ) , GROUP_CONCAT( DISTINCT( i.name ) SEPARATOR ', ')

Check the fiddle: http://sqlfiddle.com/#!2/626760/4

OTHER TIPS

From your SQL fiddle I take it that you don't want to select four rows, as you have been telling us all the while, but one record only. You are using group_concat and get duplicate strings. You can avoid this by using group_concat with DISTINCT:

SELECT r.*, 
GROUP_CONCAT(DISTINCT t.t_name SEPARATOR ', ' ) , GROUP_CONCAT(DISTINCT i.in_name SEPARATOR ', ')
FROM recipe r
JOIN recipe_has_tag rht ON rht.recipe_r_id  = r.r_id
JOIN tag t ON rht.tag_t_id = t.t_id
JOIN ingredient_has_recipe ihr ON ihr.recipe_r_id = r.r_id
JOIN ingredient i ON ihr.ingredient_in_id = i.in_id
WHERE r.r_id = 1   
GROUP BY r.r_id;

Here is the corrected SQL fiddle: http://sqlfiddle.com/#!2/626760/5.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top