SQL Inner Join With Multiple Columns - mysql

I've got 2 tables - dishes and ingredients:
in Dishes, I've got a list of pizza dishes, ordered as such:
In Ingredients, I've got a list of all the different ingredients for all the dishes, ordered as such:
I want to be able to list all the names of all the ingredients of each dish alongside each dish's name.
I've written this query that does not replace the ingredient ids with names as it should, instead opting to return an empty set - please explain what it that I'm doing wrong:
SELECT dishes.name, ingredients.name, ingredients.id
FROM dishes
INNER JOIN ingredients
ON dishes.ingredient_1=ingredients.id,dishes.ingredient_2=ingredients.id,dishes.ingredient_3=ingredients.id,dishes.ingredient_4=ingredients.id,dishes.ingredient_5=ingredients.id,dishes.ingredient_6=ingredients.id, dishes.ingredient_7=ingredients.id,dishes.ingredient_8=ingredients.id;
It would be great if you could refer to:
The logic of the DB structuring - am I doing it correctly?
The logic behind the SQL query - if the DB is built in the right fashion, then why upon executing the query I get the empty set?
If you've encountered such a problem before - one that requires a single-to-many relationship - how did you solved it in a way different than this, using PHP & MySQL?
Disregard The Text In Hebrew - Treat It As Your Own Language.

It seems to me that a better Database Structure would have a Dishes_Ingredients_Rel table, rather than having a bunch of columns for Ingredients.
DISHES_INGREDIENTS_REL
DishesID
IngredientID
Then, you could just do a much simpler JOIN.
SELECT Ingredients.Name
FROM Dishes_Ingredients_Rel
INNER JOIN Ingredients
ON Dishes_Ingredients.IngredientID = Ingredients.IngredientID
WHERE Dishes_Ingredients_Rel.DishesID = #DishesID

1. The logic of the DB structuring - am I doing it correctly?
This is denormalized data. To normalize it, you would restructure your database into three tables:
Pizza
PizzaIngredients
Ingredients
Pizza would have ID, name, and type where ID is the primary key.
PizzaIngredients would have PizzaId and IngredientId (this is a many-many table where the primary key is a composite key of PizzaId and IngredientID)
Ingredients has ID and name where ID is the primary key.
2. List all the names of all the ingredients of each dish alongside each dish's name. Something like this in MySQL (untested):
SELECT p.ID, p.name, GROUP_CONCAT(i.name) AS ingredients
FROM pizza p
INNER JOIN pizzaingredients pi ON p.ID = pi.PizzaID
INNER JOIN ingredients i ON pi.IngredientID = i.ID
GROUP BY p.id
3. If you've encountered such a problem before - one that requires a single-to-many relationship - how did you solved it in a way different than this, using PHP & MySQL?
Using a many-many relationship, since that what your example truly is. You have many pizzas which can have many ingredients. And many ingredients belong to many different pizzas.

The reason you are getting an empty result is because you are setting a join condition that never gets satisfied. During the INNER join execution the database engine compares each record of the first table with each record of the second one trying to find a match where the id of the ingredient table record being evaluated is equal to ingredient1 AND ingredient2 AND so on. It would return some result if you create a record in the first table with the same ingredient in all 8 columns (testing purposes only).
Regarding the database structure, you choose a denormalized one creating 8 columns for each ingredient. There are a lot of considerations possible on this data structure (performance, maintainability, or just think if you are asked to insert a dish with 9 ingredients for example) and I would personally go for a normalized data structure instead.
But if you want to keep this, you should write something like:
SELECT dishes.name, ingredients1.name, ingredients1.id, ingredients2.name, ingredients2.id, ...
FROM dishes
LEFT JOIN ingredients AS ingredients1 ON dishes.ingredient_1=ingredients1.id
LEFT JOIN ingredients AS ingredients2 ON dishes.ingredient_2=ingredients2.id
LEFT JOIN ingredients AS ingredients3 ON dishes.ingredient_3=ingredients3.id
...
The LEFT join is required to get a result for unmatched ingredients (0 value when no ingredient is set reading your example)

Related

Writing an SQL query to bring back results using a filter

I am trying to write an SQL query to use within a filter on a webpage. The webpage is for an online recipe cookbook.
If you are logged into the system and go to your recipes only your user can see these recipes.
So for example lets say I created 3 recipes and one has broccoli in. I want to be able to choose broccoli in the ingredients filter and for it see what recipes I have saved with broccoli and display it. So I beleive the query needs to take 2 values which is the ingredient id and the users id, it needs to check which recipe has that ingredient in it and then if the user created that recipe, it then needs to return the recipe ID so we can display those recipes.
At the moment this is my query. However it brings back everything that user has made.
SELECT quantities.recipe_id
FROM quantities, recipes
WHERE quantities.ingredient_id = '24' AND recipes.user_id = '6'
Do I need to do a join somewhere?
Table structure for Recipes:
Table Structure for Quantities
Yes you need a join. The better query would be:
SELECT quantities.recipe_id
FROM quantities
INNER JOIN recipes ON quantities.recipe_id = recipes.recipe_id
WHERE quantities.ingredient_id = 24 AND recipes.user_id = 6
Without the join, you're generating a product of every recipe row with every ingredient row, which is not what's intended.
SELECT quantities.recipe_id
FROM quantities, recipes USING(recipe_id)
WHERE quantities.ingredient_id = '24' AND recipes.user_id = '6'
An alternative (shorter code) way to join when the field is named the same in both tables.

Inner join with multiple matching records

I have 3 tables in my database: sProduct, sProductDetail and sProductDetailWarehouse. This is basically a webshop with having multiple EANs possible for a single product. For instance a t-shirt with multiple colors available, each color being it's own EAN.
The important bits about tables:
sProduct has ID which is primary key and title (varchar).
sProductDetail has ID (primary key), ID_sProduct (the correlation to the sProduct table), EAN and title
sProductDetailWarehouse has ID (primary key), ID_sProductDetail (correlation to the detail table) and stock (int).
What I would want is to use something similar to this:
select pd.ID,pd.title,pdw.stock from sProduct p
inner join sProductDetail pd on pd.ID_sProduct=p.ID
left join sProductDetailWarehouse pdw on pdw.ID_sProductDetail=pd.ID
and only have it return 1 record on join with the highest stock. The problem is I can't use order by since I have multiple products in a query needing to be ordered by their release date.
So basically out of every one sProduct.ID I would need only one sProductDetail.ID returned even though there might be many. Can anyone help with this?
Thanks.

Issue with Mysql View and/or different method?

I'm trying to make a mysql view to then be used with Sphinx search.
I have three tables: recipes, tags, ingredients.
*Bold Words are the row names
Recipes is where I store the recipe name, and the cooking time (durationtotal)
Ingredients is where I store 1 ingredient needed for the specific recipe (which means there is more than 1 ingredient corresponding to every recipe.) Ingredients has a recipe_id which corresponds to a recipe, and ingredient field where I store the name of the ingredient.
Tags is similar to Ingredients, in that for every recipe, there can be more than 1 tag. For example: Mom's Chicken Recipe has the tags(comma-delineated): yummy,chicken,thanksgiving Tags also has a recipe_id field and a tag field which is where I store the name of the tag.
Now, I'm implementing Sphinx Search and need a way to combine the
Recipe Id (recipes.id)
Recipe Name (recipes.name)
Ingredients Id (ingredients.id)
Ingredients Name (ingredients.ingredient)
Ingredients Corresponding Recipe Id (ingredients.recipe_id)
Tags Id (tags.id)
Tags Name (tags.tag)
Tags Corresponding Recipe Id (tags.recipe_id)
My thought process (I'm a pretty programmer, and just discovered Sphinx) is to create a MYSQL view that combines all of the above rows so I'd have a view that looked like this:
RecipeName: To' Good Chicken Soup......Tag:Yummy......Ingredient:Chicken
RecipeName: To' Good Chicken Soup......Tag:Delicious......Ingredient:Chicken
RecipeName: To' Good Chicken Soup......Tag:Soup......Ingredient:Chicken
RecipeName: To' Good Chicken Soup......Tag:Yummy......Ingredient:Water
The problem with this method is, as you can see, that it duplicates a lot of rows. But since this was only the method I could think of, I went ahead and created a new view with this:
SELECT
recipes.id,
recipes.name,
ingredients.ingredient,
tags.tag
FROM
recipes, ingredients, tags
WHERE
recipes.id=ingredients.recipe_id
AND recipes.id=tags.recipe_id;
I worked this code into mysql, but am only getting recipes which have tags/ingredients corresponding to them? Also, is this the right method to structure my mysql view?
Thanks for any and all help!
If you want to get all recipes regardless if they have ingredients or tags you want to use a LEFT OUTER JOIN
SELECT recipes.id, recipes.name, ingredients.ingredient, tags.tag
FROM recipes
LEFT OUTER JOIN ingredients ON recipes.id=ingredients.recipe_id
LEFT OUTER JOIN tags ON recipes.id=tags.recipe_id;
Try a LEFT JOIN:
http://www.w3schools.com/sql/sql_join_left.asp
SELECT
recipes.id,
recipes.name,
ingredients.ingredient,
tags.tag
FROM
recipes
LEFT JOIN ingredients ON recipes.id=ingredients.recipe_id
LEFT JOIN tags ON recipes.id=tags.recipe_id

New to MySQL looking for Many-to-Many relationship query

Some background I have a set of data that represents the alchemy ingredients and their effects from Skyrim. If you're unfamiliar with this you can combine 2-4 ingredients to make a potion. Each ingredient has 4 effects. If any effects between ingredients are the same it will make that type of potion. I've identified this as a many-to-many relationship and I set up my tables like so:
ingredients: ing_id (key), ing_name, (other supplemental info)
effects: eff_id (key), eff_name
ing_eff_xref: eff_id, ing_id
I would like to input 2 or more available ingredients and return possible combinations without knowing what the effects are. My sql experience is pretty much limited to phpmyadmin and simple select queries. I guess my questions are: is this the right way to structure the tables for this type of relationship, do I need to set foreign keys if I don't plan on updating the tables, and is there a query that can take a set of ing_names and return only eff_names that intersect?
Here is the mysqldump of the db if anyone is interested: http://dl.dropbox.com/u/59699040/alchemy_db.sql
is this the right way to structure the tables for this type of relationship?
Yes, but then you don't need to have effect1 through effect4 on the ingredient table.
do I need to set foreign keys if I don't plan on updating the tables?
Yes. The only way for you to get the data that you're after is by JOINing three tables together. Without foreign keys (or more specifically, appropriate indexes), that may not perform well on queries. Of course you do have a small number of rows overall, but using foreign keys is a good practice to follow in this type of scenario.
is there a query that can take a set of ing_names and return only
eff_names that intersect?
I think you're after something like this:
SELECT e.eff_name
FROM ingredients i
INNER JOIN ing_eff_xref ie ON ie.ing_id = i.ing_id
INNER JOIN effects e ON e.eff_id = ie.eff_id
WHERE i.ing_name = 'Abecean Longfin ';
If you need to see effects for multiple ingredients, you could adjust your WHERE clause, like this:
WHERE i.ing_name IN ('Abecean Longfin ','Eye of Sabre Cat ','Bear Claws ');
You'll probably not want duplicate effects, so you could do a SELECT DISTINCT to eliminate those.
Can potion effects stack in Skyrim? If they in can stack, then you can do a GROUP BY query with a COUNT to get the stacked value of each effect:
SELECT e.eff_name, count(*) as value
FROM ingredients i
INNER JOIN ing_eff_xref ie ON ie.ing_id = i.ing_id
INNER JOIN effects e ON e.eff_id = ie.eff_id
WHERE i.ing_name IN ('Eye of Sabre Cat ','Bear Claws ')
GROUP BY e.eff_name;
This query will list 6 effects with a value of 1, and "Restore Stamina" will have a value of 2. Not sure if Skyrim potions work this way or not, but it was just an extra thought.

Joins on MySQL many-to-many tables

This has been driving me mad.
I have three tables:
items
ID
name
type
cats
ID
name
items_to_cats
FK_ITEM_ID
FK_CAT_ID
This is a simple many-to-many relationship. I have items and categories. Each item can be linked to one or more categories. This is done via a simple joining table where each row maintains a relationship between one item and one category using foreign key constraints.
You will notice that my "items" table has a field called "type". This is an indexed column that defines the type of content stored there. Example values here are "report", "interview", "opinion", etc.
Here's the question. I want to retrieve a list of categories that have at least one item of type "report".
Ideally I want to get the result in a single query using joins. Help!
select distinct cats.id, cats.name
from cats
join items_to_cats on items_to_cats.fk_cat_id=cats.id
join items on items.id=items_to_cats.fk_item_id
where items.type='report'
Just as a point of database design, if you have a small set of legal values for items.type, i.e. "report", "interview", "opinion", maybe a couple more, then you really should create a separate table for that with, say, an id and a name, then just put the type id in the items table. That way you don't get into trouble because somewhere it's mis-spelled "raport", or even more likely, someone puts "reports" instead of "report".
or how about this :
SELECT c.id, c.name
FROM cats c
WHERE c.id IN
(SELECT ic.fk_cat_id
FROM items_to_cats ic
JOIN items i on i.id=ic.fk_item_id
WHERE items.type='report'
)