SQL: exact row match given certain elements - mysql

this is my first time posting here. I don't seem to find the answer to my problem.
So... I'm arranging a DB for a school project, a cookbook that only shows recipes that can be made with existing elements from the "shelf".
These ingredients have to have an exact ingredients match.
user:
+---------------+------+----------+----------+
| email | name | lastname | password |
+---------------+------+----------+----------+
| pal#mail.com | John | Potato | password |
| they#mail.com | Mary | Carrot | password |
+---------------+------+----------+----------+
shelf:
+---------+------------+---------------+
| shelfID | ingredient | user |
+---------+------------+---------------+
| 1 | 1 | pal#mail.com |
| 2 | 2 | pal#mail.com |
| 3 | 3 | pal#mail.com |
| 4 | 4 | pal#mail.com |
| 5 | 10 | they#mail.com |
| 6 | 12 | they#mail.com |
+---------+------------+---------------+
This is my recipe_ingredient relationship table
recipe_ingredient:
+--------+------------+
| recipe | ingredient |
+--------+------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 4 |
| 1 | 10 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 2 | 4 |
| 3 | 2 |
| 3 | 3 |
| 3 | 15 |
+--------+------------+
I've tried this query:
SELECT
rec_ing.recipe, shf.ingredient, shf.user
FROM
recipes_ingredients AS rec_ing
INNER JOIN
shelf AS shf ON rec_ing.ingredient = shf.ingredient
INNER JOIN
users AS usr ON shf.user = usr.email
WHERE
usr.email = 'pal#mail.com'
that returns this table:
+--------+------------+--------------+
| recipe | ingredient | user |
+--------+------------+--------------+
| 1 | 1 | pal#mail.com |
| 1 | 2 | pal#mail.com |
| 1 | 4 | pal#mail.com |
| 2 | 1 | pal#mail.com |
| 2 | 2 | pal#mail.com |
| 2 | 3 | pal#mail.com |
| 2 | 4 | pal#mail.com |
| 3 | 2 | pal#mail.com |
| 3 | 3 | pal#mail.com |
+--------+------------+--------------+
Although it's true that the Recipe 1 contains ingredients from my shelf, it's also missing Ingredient 10
+--------+------------+
| recipe | ingredient |
+--------+------------+
| 1 | 1 |
| 1 | 2 |
| 1 | 4 |
| 1 | 10 |
| ... | ... |
+--------+------------+
I'm trying to only get this kind of result set.
+--------+------------+
| recipe | ingredient |
+--------+------------+
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 2 | 4 |
+--------+------------+
Because Recipe 2 contains all elements from my shelf
I have been going around all day with this problem ... What could be missing?

This is a bit tricky, because you need to aggregate your current query by recipe, but you also need the original query to get back the full records. Sadly, MySQL does not support common table exprrssions or other features which could give us a less verbose query.
SELECT
rec_ing.recipe,
shf.ingredient,
shf.user
FROM recipes_ingredients AS rec_ing
INNER JOIN shelf AS shf
ON rec_ing.ingredient = shf.ingredient
INNER JOIN users AS usr
ON shf.user = usr.email
INNER JOIN
(
SELECT rec_ing.recipe
FROM recipes_ingredients AS rec_ing
INNER JOIN shelf AS shf
ON rec_ing.ingredient = shf.ingredient
LEFT JOIN users AS usr
ON shf.user = usr.email
WHERE
usr.email = 'pal#mail.com'
GROUP BY rec_ing.recipe
HAVING COUNT(usr.email) = COUNT(*)
) t
ON rec_ing.recipe = t.recipe
WHERE
usr.email = 'pal#mail.com'
The basic strategy here is to just do one additional join to a subquery which identifies all recipes where every ingredient belongs to a given user. The critical part is the following:
HAVING COUNT(usr.email) = COUNT(*)
This checks that the total number of rows for a given recipe matches the number of rows which have been assigned to a given user.

Related

Mysql, repeat row by the max count of details

I have two tables
Master table
|-----------|--------|------------|
| id_client | name | ci |
|-----------|--------|------------|
| 1 | B | 123 |
| 2 | A | 234 |
| 3 | C | 345 |
|-----------|--------|------------|
Detail of equipments
|-----------|--------------|
| id_client | id_equipment |
|-----------|--------------|
| 1 | 1 |
| 1 | 2 |
| 2 | 3 |
|-----------|--------------|
The result that I expected is:
All clientes with or without equipments
|-----------|----------------|----------------|
| id_client | id_equipment | numberEquipment|
|-----------|----------------|----------------|
| 1 | 1 | Equip1 |
| 1 | 2 | Equip2 |
| 2 | 3 | Equip1 |
| 2 | - | Equip2 |
| 3 | - | Equip1 |
| 3 | - | Equip2 |
|-----------|----------------|----------------|
Client 1 have two equipments and this is the max cant of equipments assigned, then, all clientes with minor than two have to fill the detail empty two times. client 2 have one equipment assigned, the second will be empty, and cliente 3 doesnt have any equipment, then the two equipments will be empty.
I was implemented crosstab on jasperreport, i have to define de columnGroup, when the name of group Equipment1, equipment2... etc, doesnt exist on any row this column group doesnt show.
I hope someone can helpme.
Thanks.
SELECT CONCAT('EQUIPMENT ',g.ennum) AS numberEquipment,
g.ID_CLIENTE as id_client, g.ID_EQUIPO as id_equipment,
(SELECT #running1:= 0) AS running1,(SELECT #previous1:= 0) AS previous1
FROM (
SELECT c.ID_CLIENTE, eq.ID_EQUIPO
#running1:=if(#previous1=concat(eq.ID_CLIENTE),#running1,0) + 1 as ennum, #previous1:=concat(eq.ID_CLIENTE)
FROM CLIENTE AS c
LEFT JOIN detail_equipments AS eq ON eq.ID_CLIENTE = c.ID_CLIENTE
ORDER BY c.ID_CLIENTE ) AS g
GROUP BY g.ID_CLIENTE, g.ID_EQUIPO
ORDER BY ID_CLIENTE, g.ennum
this show
|-----------|----------------|----------------|
| id_client | id_equipment | numberEquipment|
|-----------|----------------|----------------|
| 1 | 1 | Equip1 |
| 1 | 2 | Equip2 |
| 2 | 3 | Equip1 |
| 3 | - | Equip1 |
|-----------|----------------|----------------|

Update data in Multiple table

Three table as follow
mysql> select * from food;
+--------+------+-------+
| foodid | name | price |
+--------+------+-------+
| 1 | 雞 | 100 |
| 2 | 鴨 | 200 |
| 3 | 魚 | 300 |
| 4 | 肉 | 400 |
+--------+------+-------+
4 rows in set
mysql> select * from drink;
+---------+------+-------+
| drinkid | name | price |
+---------+------+-------+
| 1 | 紅茶 | 50 |
| 2 | 綠茶 | 100 |
| 3 | 奶茶 | 150 |
+---------+------+-------+
3 rows in set
mysql> select * from order_table;
+----+-----------+--------+---------+------------+-------------+-------------+
| id | user_name | foodid | drinkid | food_count | drink_count | total_price |
+----+-----------+--------+---------+------------+-------------+-------------+
| 2 | 小明 | 3 | 2 | 2 | 2 | 0 |
| 3 | 小華 | 1 | 1 | 1 | 8 | 0 |
| 4 | 小英 | 1 | 3 | 3 | 3 | 0 |
| 6 | 小a | 2 | 1 | 4 | 6 | 0 |
| 7 | 小b | 2 | 2 | 5 | 4 | 0 |
| 8 | 小c | 2 | 3 | 6 | 10 | 0 |
| 9 | 大A | 3 | 1 | 9 | 8 | 0 |
| 10 | 大B | 3 | 2 | 5 | 4 | 0 |
| 11 | 大C | 3 | 3 | 10 | 3 | 0 |
+----+-----------+--------+---------+------------+-------------+-------------+
foodid in order_table is link to foodid in food table,
drinkid in order_table is link to drinkid in drink table,
Now, I want to calculate total price,
Total_price =
order_table.foodid(food.price in food table) * order_table.food_count +
order_table.drinkid(drink.price in drink table) * order_table.drink_count;
So, let me knowlege the command to update total price
thx a lot.
The reason why I used LEFT JOIN on the following query is because I assumed that some orders may only contain drinks or foods.
UPDATE order_table a
LEFT JOIN food b
ON a.foodid = b.foodID
LEFT JOIN drink c
ON a.drinkID = c.drinkID
SET a.total_price = (a.food_count * COALESCE(b.price, 0) +
a.drink_count * COALESCE(c.price, 0))
SQLFiddle Demo
To further gain more knowledge about joins, kindly visit the link below:
Visual Representation of SQL Joins
Something like this should be close:
SELECT COALESCE(F.Price,0)*OT.Food_Count+COALESCE(D.Price,0)*OT.Drink_Count Total_price
FROM Order_Table OT
LEFT JOIN Food F ON OT.FoodId = F.FoodId
LEFT JOIN Drink D ON OT.DrinkId = D.DrinkId
And to actually update that column:
UPDATE Order_Table OT
LEFT JOIN Food F ON OT.FoodId = F.FoodId
LEFT JOIN Drink D ON OT.DrinkId = D.DrinkId
SET OT.Total_Price = COALESCE(F.Price,0)*OT.Food_Count+COALESCE(D.Price,0)*OT.Drink_Count

How should I write this MySQL query containing multiple Left Joins

I have a query consisting of multiple joins and I am wondering whether it can be re-written to improve performance.
I have 2 tables as follows (I have removed non-important columns for this example):
slots
------------------------------------------
| id | name | slot_1 | slot_2 | slot_3 |
------------------------------------------
| 1 | Bob | 1 | 2 | 3 |
| 2 | Jim | 4 | 3 | 3 |
| 3 | Alf | 1 | 2 | 5 |
------------------------------------------
(There are 25 slots in total, each in it's own column)
slot_details
-----------------------------------
| id | stat_1 | stat_2 | stat_3 |
-----------------------------------
| 1 | 1 | 5 | 6 |
| 2 | 4 | 31 | 23 |
| 3 | 6 | 5 | 7 |
| 4 | 7 | 4 | 9 |
| 5 | 2 | 3 | 5 |
-----------------------------------
(There are 10 stats in total)
The query is as follows:
SELECT
slots.name,
slot_1_details.stat_1 AS slot_1_stat_1,
slot_1_details.stat_2 AS slot_1_stat_2,
slot_1_details.stat_3 AS slot_1_stat_3,
slot_2_details.stat_1 AS slot_2_stat_1,
slot_2_details.stat_2 AS slot_2_stat_2,
slot_2_details.stat_3 AS slot_2_stat_3,
slot_3_details.stat_1 AS slot_3_stat_1,
slot_3_details.stat_2 AS slot_3_stat_2,
slot_3_details.stat_3 AS slot_3_stat_3
FROM
slots
LEFT JOIN
slot_details AS slot_1_details
ON (
slot_1_details.id = slots.slot_1
)
LEFT JOIN
slot_details AS slot_2_details
ON (
slot_2_details.id = slots.slot_2
)
LEFT JOIN
slot_details AS slot_3_details
ON (
slot_3_details.id = slots.slot_3
)
WHERE (
slots.id = 1
)
The expected outcome of this query would be as follows:
| name | slot_1_stat_1 | slot_1_stat_2 | slot_1_stat_3 | slot_2_stat_1 | slot_2_stat_2 | slot_2_stat_3 | slot_3_stat_1 | slot_3_stat_2 | slot_3_stat_3 |
|bob | 1 | 5 | 6 | 4 | 31 | 23 | 6 | 5 | 7 |
Unfortunately I am not in a situation where I can change the tables.
Thank you for the help!
maybe
SELECT * FROM slots s LEFT JOIN slot_details sd ON s.id=sd.id
but i'm not sure because the query you posted is very confusing.
what are the keys of those tables?

Include third table in LEFT JOIN query

I have five mysql tables. shops
+----+--------------+--------------+
| id | name | address |
+----+--------------+--------------+
| 1 | Shop1 | Street1 |
| 2 | Shop2 | Street2 |
| 3 | Shop3 | Street3 |
| 4 | Shop4 | Street4 |
+----+--------------+--------------+
fruits
+----+--------------+--------------+
| id | fruit | price |
+----+--------------+--------------+
| 1 | Bannana | 2.5 |
| 2 | Apple | 2.1 |
| 3 | Orange | 1.8 |
| 4 | Plum | 2.2 |
+----+--------------+--------------+
availability
+----+--------------+--------------+
| id | shop_id | fruit_id |
+----+--------------+--------------+
| 1 | 1 | 2 |
| 2 | 2 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 1 |
+----+--------------+--------------+
shop_activity
+----+--------------+--------------+--------------+
| id | shop_id | user_id | status |
+----+--------------+--------------+--------------+
| 1 | 2 | 1 | 1 |
| 2 | 3 | 2 | 1 |
| 3 | 1 | 2 | 2 |
| 4 | 2 | 2 | 1 |
+----+--------------+--------------+--------------+
users
+----+--------------+
| id | name |
+----+--------------+
| 1 | Peter |
| 2 | John |
+----+--------------+
I have query
SELECT
availability.shop_id,
shops.name
FROM availability
LEFT JOIN shops
ON availability.shop_id=shops.id
WHERE
fruit_id = 2
As a result I get name list of shops where fruit with id 2 (apple) is available.
What should I do so that I can include shop_activity table in query to get user's status if users.id = 1 beside proper shop. Something like this...
Shop1, NULL
Shop2, status: 1
You could try something like this:
SELECT
availability.shop_id,
shops.name,
shop_activity.status
FROM availability
LEFT JOIN shops
ON availability.shop_id=shops.id
LEFT JOIN shop_activity
ON shop_activity.shop_id = availability.shop_id
and shop_activity.user_id = 1
WHERE
fruit_id = 2
SELECT
availability.shop_id,
shops.name
FROM shops
LEFT JOIN availability ON availability.shop_id=shops.id
LEFT JOIN shop_activity ON shop_activity .shop_id=shops.id
WHERE
fruit_id = 2
and users.id=1
try making shops as the first table in left join
Try the following:
SELECT shops.name, shop_activity.status
FROM shops
INNER JOIN availability ON availability.shop_id = shops.id
AND availability.fruit_id = 2
LEFT JOIN shop_activity ON shops.shop_id = shop_activity.shop_id
AND shop_activity.user_id = 1
This should give you a row for every shop with apples, but the status will show as null for shops where the user has no activity, otherwise shows the status of that user.

mysql - join problem

I have two tables like below,
mysql> select * from Books ;
+----+------+------------+----------+----------+
| id | name | author_name| category | category2|
+----+------+------------+----------+----------+
| 1 | 1 | Steve | CT001 | CT003 |
| 2 | 2 | John | CT002 | CT002 |
| 3 | 3 | Larry | CT003 | CT002 |
| 4 | 3 | Michael | CT004 | CT004 |
| 5 | NULL | Steven | CT005 | CT005 |
+----+------+------------+----------+----------+
mysql> select * from Codemst ;
+----+------+------------+
| id | code | name |
+----+------+------------+
| 1 | CT001| fiction |
| 2 | CT002| category1 |
| 3 | CT003| etc |
| 4 | CT004| etc2 |
| 5 | CT005| etc3 |
+----+------+------------+
I want to get human readable category name when I query like "select * from Books;"
If there was only one category in the Books table, I think I can use "Join" but, in this case what can I do?
select * from Books b
Inner Join Codemst c1 on b.category = c1.code
inner join codemst c2 on b.category2 = c2.code;
c1.name will hold the readable category, and c2.name the readable category2