I have table pizza. It includes fields like cost, id, name (of pizza), and ingredients.
I also have table ingredients, with name of ingredient, and id. How to i put in table pizza ingedients for example 1,2,4,15, and be able to get that ingredients name from table ingredients?
Table Pizza:
id name cost ingredients
1 Vegie Pizza 12,59 1,2
Table Ingredients
id name
1 cheese
2 broccoli
3 pepperoni
I would like to get for egzample name and ingredients:
Vegie Piza - cheese, broccoli - 12,59
Or be able to order Vegie Pizza with pepperoni.
How do i connect this two tables in a way I described?
First of all donot store relations as comma separated values instead use junction table to relate 2 entities for this see Database Normalization
For your current solution you need to use find_in_set in join condition
select p.id,group_concat(i.name) ingredients,p.cost
from
Pizza p
join Ingredients i on(find_in_set(i.id,p.ingredients) > 0)
group by p.id
Fiddle Demo
You should have a table PizzaIngredients, with one row per pizza and per ingredient in the table. Someones, one doesn't have control over the data structure being used. If so, there is a solution in MySQL:
select p.name, p.cost, group_concat(i.name)
from pizza p join
ingredients i
on find_in_set(i.id, p.ingredients) > 0
group by p.name, p.cost;
However, a junction/association table is a much better way to store such data in a relational database.
Related
I am trying to solve this problem. I'll illustrate it with how fruits relate to recipes.
Background Info:
there are explicit links between fruits and recipes (very few values 1:1)
implicit links (same metadata between fruit and recipe) which could be 1:m
Fruit: Texture/Color
Recipe: Texture/Color.../Meal_Type
Essentially for the fruit aspect I have a query that joins several tables (ex.10), let's call this the fruit table.
For the recipe table, I have a query that joins a few table (ex.6).
I want to see for a fruit, how many recipes are possible. So Essentially, I want to join these 2 queries based on the texture/color fields to see the possible meal types. A fruit could have multiple recipes. I'm trying to use texture/color as a foreign key as if a fruit has the same metadata texture/color it should be linked.
Ex.
example logic
In this example, for the soft red fruit, 3 meals are possible: breakfast, lunch and a snack. For a hard orange fruit, 2 meals are possible: lunch and dessert.
How would I do this?
I tried to create a temporary table/view but I only have readonly access so I need another way.
First, we can query the associated records from the Fruit and Recipe tables using a SQL JOIN on the custom condition or relationship as described (match fruits with recipes having the same color and texture):
SELECT *
FROM Fruit f
JOIN Recipe r
ON f.Texture = r.Texture
AND f.Color = r.Color
This will return a result set containing a row for each matching pair of records, containing all columns from both tables. Each row will be a matching combination of Fruit and Recipe.
If we simply want to count how many unique meal times (how many distinct Meal_Types) for each fruit, we can select an aggregate count as follows:
SELECT f.ID, COUNT(DISTINCT r.Meal_Type) As Meals
FROM Fruit f
JOIN Recipe r
ON f.Texture = r.Texture
AND f.Color = r.Color
GROUP BY f.ID
The above query will return a result set containing a row for each fruit and the number of unique Meal_Types counted for that particular fruit. We use DISTINCT to only count each Meal_Type once, and GROUP BY f.ID to distinguish the individual counts (in our case, we're counting Meal_Type by Fruit.ID).
I'm new to mysql and I have to do a kitchen inventory database with recipes for a school project. I'm trying to get a query that shows recipes and users when users have all the ingredients to that recipe. I'm trying to work with the following tables for this query:
Products
ProdID
name (brand included)
tag - FK
Tags (Since there are different brands of products, a tag table to associate each product to a generic type, like orange juice)
TagID
name
Recipes
RecipeID
Name
Description
Ingredients (To associate all the ingredients with each recipe)
IgrID
TagID - FK
RecipeID - FK
Inventory
InvID
UserID
ProductID - FK
QTY
My idea was to get all the ingredients a user have and then check if they complete a recipe, but I don't know how to put together all the tables to get the first part. I tought of doing a join with users ingredients and recipes for the second part, I don't know if it would work though. Maybe the structure I made is unnecessarily complex too. Could anyone give me a hand?
So I have 2 tables. 1 called "recipes" and one called "ingredients". In the recipes table I have: id, recipe_name, calories, bld (an enumerated list that can be B, L, or D for breakfast, lunch and dinner), prep (prep time), and skill (another enumerated list for beg, int, and adv).
In the ingredients table I have: recipe_id (with a foreign key constraint to the id column in the recipes table), and ingredient_name, and some other columns.
Im trying to select all of the columns from the recipes table and the ingredient_name comlumn from the ingredients table with some conditions - I want to return all recipes, even if they dont have ingredients in the ingredient table (this wouldnt really happen in practice but this is for me to learn anyway), and I want to exclude recipes if one of their ingredients includes part of a keyword the user enters (in the next example ill be using "olive" as the keyword).
Here's my SQL so far:
SELECT recipes.*, ingredients.ingredient_name
FROM recipes
LEFT OUTER JOIN ingredients
ON ingredients.recipe_id=recipes.id
WHERE ingredients.ingredient_name <> '%olive%'
ORDER BY RAND() LIMIT 7
This only returns the recipes with ingredients and is still including recipes where "olive" is part of one of the names of the ingredients.
Thanks for any help
It's best, when working with this type of logic, to think in sets of data.
Set 1: List of recipes where Olive is in the ingredients:
SELECT recipe_id
FROM ingredients
WHERE ingredient_name LIKE '%olive%'
Now we have a set of recipes that we can exclude because they have Olive in them.
SET 2: List of recipes and their ingredients:
SELECT recipes.*, ingredients.ingredient_name
FROM recipes
LEFT OUTER JOIN ingredients
ON ingredients.recipe_id=recipes.id
No surprises here. Just a set of everything. What we want to do is restrict this set, by the set in the previous step:
SET 3: Restrict SET2 set with SET1 set:
SELECT recipes.*, ingredients.ingredient_name
FROM recipes
LEFT OUTER JOIN ingredients
ON ingredients.recipe_id=recipes.id
WHERE recipes.id NOT IN
(
SELECT recipe_id
FROM ingredients
WHERE ingredient_name LIKE '%olive%'
)
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)
I have a table called 'recipes' that lists, in a SET type called 'ingredients', all of the foods used in a recipe (recipes are restricted to using a small number of specific ingredients).
recipes table
id (int)
recipeName (text)
ingredients (SET)
I have another table called 'food_types'. Each record has a 'name' field (e.g. 'fruit', 'poultry', 'spices', 'meat', ...) and a SET type called 'items' that holds the ingredients belonging to that food type. E.g. for the 'fruit' record the SET might be 'apple,orange,pear,banana'.
'
food_types table
id (int)
category (text)
items (SET)
My problem is how to create a MySQL query that selects all records from the 'recipes' table that has one or more item from the given 'items' set in the 'ingredients' set.
So in English my query might be:
'Select all recipes where the ingredients contains one or more fruit'
Can anyone suggest a good way to do this with a MySQL query?
NOTE: I appreciate that there are better ways to structure the tables for a recipes/ingredients problem but I am looking for a MySQL query solution that does this with SETs and the tables described above.
This is an inappropriate use of a SET field - you can't add a food_type without having to do a schema modification, always a good indicator that your structure is wrong. Use a join table to represent a many:many relationship. In this case, create a table ingredient_type that refers to both tables with ingredient_id and food_type_id. This also allows you to have ingredients with multiple types, like a SET does, but much more flexible.
ingredient (ingredient):
apple
orange
chicken
ginger
potato
food_type (type):
fruit
vegetable
meat
poultry
spice
ingredient_type (ingredient, type)
apple, fruit
orange, fruit
chicken, meat
chicken, poultry
ginger, vegetable
ginger, spice
potato, vegetable
recipe_ingredient (recipe, ingredient):
recipe1, chicken
recipe1, potato
recipe2, orange
recipe2, ginger
With that structure, you can do queries like this:
SELECT DISTINCT(recipe) FROM recipe_ingredient WHERE ingredient = 'orange';
That will show you all recipes that use oranges. Slightly more complex:
SELECT DISTINCT(recipe) FROM recipe_ingredient JOIN ingredient_type on recipe_ingredient.ingredient = ingredient_type.ingredient WHERE ingredient_type.type = 'fruit';
That will find all recipes that use any ingredient that is a fruit.
EDIT: On using SETs.
You can do this by making use of the binary nature of SET storage. Where there is an overlap between recipe ingredients and category members, the number of set bits in a bitwise AND (using the & operator) will be > 0:
SELECT DISTINCT recipeName FROM recipes JOIN food_types ON BIT_COUNT(food_types.items & recipes.ingredients) > 0 WHERE food_types.category = 'fruit';
This will only work if the ingredients and items set definitions are absolutely identical.
There are other problems with SETs:
they can only contain up to 64 items
field values are always in the same order as the set
most queries other than simple matches (for example to match on a single item within a set) are not indexed
unless definitions are identical, SETs can't be compared
There is nowhere to put additional metadata about each item
This approach defeats most of the point of using a relational database. This is textbook first-normal-form database stuff. The only purpose I can see for a structure like this is to use as an example of how not to do it.