SQL query improvement (extra column in table) - mysql

I have this query in SQL that I KNOW it is horribly written. Could you guys help me write it in a decent, normal person manner?
Thanks.
select distinct R.*, X.LIKED
from Recipe R
left join (select distinct R.* , '1' as LIKED
from Recipe R, Likes L
where R.id = L.idRecipe
and L.email = 'dvader#deathstar.galacticempire') X
on R.id = X.id

looks like you need all from recipe with marks on liked by vader#deathstar.galacticempire
select R.*, likedR.LIKED
from Recipe R
left join (select distinct R.id , '1' as LIKED
from Recipe R
inner join Likes L on R.id = L.idRecipe
where
L.email = 'dvader#deathstar.galacticempire') likedR
on R.id = likedR.id

Thanks everyone for your help.
I was able to do what i wanted with this query:
select distinct R.*, X.LIKED, U.imgUrl
from User U, Recipe R
left join
(select distinct R.* , '1' as LIKED
from Recipe R, Likes L
where R.id = L.idRecipe and L.email = 'dvader#deathstar.ge') X
on R.id = X.id
where R.email = U.email
This will bring all the info i need in one table plus 1 extra column with either a 1 or a null if the entry of dvader is in another table, Using joins.

Related

when i try to count the number of likes instead it shows the number of ingredients used in the recipe

This is my mysql query
SELECT r.RECIPE_ID as recipe_id,
r.RECIPE_NAME as recipe_name,
r.RECIPE_DESC as recipe_desc,
r.RECIPE_DURATION duration,
group_concat(i.ING_NAME separator ', ') as recipe_ingredients,
rc.CUSINE_NAME as cusine,
rt.RECIPE_TYPE_NAME as type,
r.image ,
count(lr.likecount)
FROM RECIPE_LIST r
inner join likerecipes lr on r.recipe_id = lr.recipe_id
inner JOIN RECIPE_INGLIST ri ON r.RECIPE_ID = ri.recipe_id
inner JOIN ING_LIST i ON ri.ING_ID = i.ING_id and r.RECIPE_ID = ri.recipe_id
inner JOIN RECIPE_CUSINE rc ON r.RECIPE_CUSINE_ID = rc.CUSINE_ID
inner JOIN RECIPE_TYPE rt ON r.RECIPE_TYPE_ID = rt.recipe_typeid
WHERE r.RECIPE_ID >= 1
GROUP BY r.RECIPE_NAME
The output table is displayed below
Image1
But when i remove the ingredients field and all its joins and execute the same query
SELECT r.RECIPE_ID as recipe_id,
r.RECIPE_NAME as recipe_name,
r.RECIPE_DESC as recipe_desc,
r.RECIPE_DURATION duration,
rc.CUSINE_NAME as cusine,
rt.RECIPE_TYPE_NAME as type,
r.image ,
count(lr.likecount)
FROM RECIPE_LIST r
inner join likerecipes lr on r.recipe_id = lr.recipe_id
inner JOIN RECIPE_CUSINE rc ON r.RECIPE_CUSINE_ID = rc.CUSINE_ID
inner JOIN RECIPE_TYPE rt ON r.RECIPE_TYPE_ID = rt.recipe_typeid
WHERE r.RECIPE_ID >= 1
GROUP BY r.RECIPE_NAME
I get the desired result ->
Image 2
Is there any way that i can display the right amount of likes along with the ingredients.
(PS. i know that the first query is showing no. of ingredients instead of likes because when i deduced one of the ingredients the like amount decreased. also the recipes displayed are displayed through a pivot table recipe_inglist joining recipe_list and ing_list table(if that had anything to do with it))
Aggregate the likes before doing the rest of the joins:
FROM RECIPE_LIST r JOIN
(SELECT lr.recipe_id, COUNT(*) as likes
FROM likerecipes lr
GROUP BY lr.recipe_id
) lr
ON r.recipe_id = lr.recipe_id . . .

SQL - Does an object have all the required components?

I'm not quite sure how to phrase the question in order to really get across what I mean, so I suppose the following example illustrates the question.
Let's say I have a recipe website where users can sign up (data stored in a Users table, with user ID being the primary key) and record ingredients (global ingredient book stored in an AllIngredients table, with ingredient ID being the primary key) that they have in their cabinet (data stored in a UserCabinet table, which links to user ID and ingredient ID).
Then, let's say I have a collection of recipes (stored in a Recipes table, with a recipe ID being the primary key) which are made up of a set of ingredients (stored in a RecipeIngredients table, which links to recipe ID and ingredient ID).
In this scenario, the question I'm asking is how do I determine which recipes a user has all of the ingredients for? They might have more ingredients than the recipe calls for, which is fine, but they can't have less (i.e. they can't be missing any). Is this possible with only SQL, or does it require several queries / manipulation using a programming language?
edit: The following is the SQL to create the sample tables I'm talking about: http://pastebin.com/N9pqmC2r
select r.*
from recipes r
join recipeComponents rc on rc.recipe_id = r.id
join userCabinet uc on uc.ingredient_id = rc.ingredient_id
where uc.user_id = ?
group by r.id
having count(uc.ingredient_id) = (
select count(*)
from recipeComponents rc1
where rc1.recipe_id = r.id
)
Or
select distinct r.*
from recipes r
join recipeComponents rc on rc.recipe_id = r.id
join userCabinet uc on uc.ingredient_id = rc.ingredient_id
where uc.user_id = ?
and not exists (
select *
from recipeComponents rc1
where rc1.recipe_id = r.id
and not exists (
select *
from userCabinet uc1
where uc1.ingredient_id = rc1.ingredient_id
)
)
Or
select r.*
from recipes r
left join (
select rc.recipe_id
from recipeComponents rc
left join userCabinet uc
on uc.user_id = ?
and uc.ingredient_id = rc.ingredient_id
where uc.ingredient_id is null
) u on u.recipe_id = r.id
where u.recipe_id is null
select distinct u.user_id, r.recipe_id
from recipeComponents r
left join userCabinet u on r.ingredient_id = u.ingredient_id
where recipe_id not in (
select recipe_id
from recipeComponents r
left join userCabinet u on r.ingredient_id = u.ingredient_id
where u.user_id is null
)

Selecting All Rows in SQL, With Specific Criteria

I have a fairly complicated SQL statement I am working on. Here is where I am at:
SELECT c.category
FROM master_cat as c
LEFT JOIN
(
SELECT cat_id, user_id COUNT(cat_id) favoriteCat
FROM ratings
GROUP BY user_id
) a ON a.cat_id= c.cat_id
LEFT JOIN users AS u
ON u.user_id AND a.user_id
WHERE u.username = '{$user}' LIMIT 1
This statement is incomplete. I am missing a middle table here. cat_id is not actually in ratings. But items_id is from a table called items and cat_id is also in that table as well.
So what I am trying to do is this:
SELECT rating FROM ??? GROUP BY cat_id where u.user=$user
The only thing I can think of doing maybe is another LEFT join with items inside favoriteCat but I am not sure if that is allowed.
I was overthinking this, here is my final solution:
SELECT c.category, count(r.rating) AS totalCount
FROM ratings as r
LEFT JOIN items AS i
ON i.items_id = r.item_id
LEFT JOIN users AS u
ON u.user_id = r.user_id
LEFT JOIN master_cat AS c
ON c.cat_id = i.cat_id
WHERE r.user_id = '{$user_id}'
GROUP BY c.category
ORDER BY totalCount DESC

Is there a better way to optimize this query?

How can I optimize this query? If you need the table structure just let me know, but this is being done as a custom plugin for Vanilla forums
SELECT
G.*
FROM
(
SELECT
D.DiscussionID,
A. NAME AS Category,
D. NAME,
U. NAME AS USER,
D.CountComments,
IFNULL(
MAX(C.DateInserted),
D.DateInserted
) AS Updated
FROM
GDN_Discussion AS D
LEFT OUTER JOIN GDN_Comment AS C ON D.DiscussionID = C.DiscussionID
INNER JOIN GDN_Category AS A ON A.CategoryID = D.CategoryID
LEFT OUTER JOIN GDN_User AS U ON U.UserID = D.InsertUserID
WHERE
A.CategoryID = '626'
GROUP BY
D.DiscussionID
) AS G
ORDER BY
G.Updated DESC
LIMIT 0, 10

MySQL query optimization

Just wondering what's a better way to write this query. Cheers.
SELECT r.user_id AS ID, m.prenom, m.nom
FROM `0_rank` AS l
LEFT JOIN `0_right` AS r ON r.rank_id = l.id
LEFT JOIN `0_user` AS m ON r.user_id = m.id
WHERE r.section_id = $section_id
AND l.rank = '$rank_name' AND depart_id IN
(SELECT depart_id FROM 0_depart WHERE user_id = $user_id AND section_id = $section_id)
GROUP BY r.user_id
Here are the table structures:
0_rank: id | section_id | rank_name |
other_stuffs
0_user: id | prenom | nom | other_stuffs
0_right: id | section_id | user_id |
rank_id | other_stuffs
0_depart: id | section_id | user_id | depart_id
| other_stuffs
The idea is to use the same in a function like:
public function usergroup($section_id,$rank_name,$user_id) {
// mysql query goes here to get a list of appropriate users
}
Update: I think I have not been able to express myself clearly earlier. Here is the most recent query that seems to be working.
SELECT m.id, m.prenom, m.nom,
CAST( GROUP_CONCAT( DISTINCT d.depart ) AS char ) AS deps,
CAST( GROUP_CONCAT( DISTINCT x.depart ) AS char ) AS depx
FROM `0_rank` AS l
LEFT JOIN `0_right` AS r ON r.rank_id = l.id
LEFT JOIN `0_member` AS m ON r.user_id = m.id
LEFT JOIN `0_depart` AS d ON m.id = d.user_id
LEFT JOIN `0_depart` AS x ON x.user_id = $user_id
WHERE r.section = $section_id
AND l.rank = '$rank_name'
GROUP BY r.user_id ORDER BY prenom, nom
Now I want to get only those result, where all entries of deps are present in entries in depx.
In other term, every user is associated with some departs. $user_id is also an user is associated with some departs.
I want to get those users whose departs are common to the departs of $user_id.
Cheers.
Update
I'm not sure without being able to see the data but I believe this query will give you the results you want the fastest.
SELECT m.id, m.prenom, m.nom,
CAST( GROUP_CONCAT( DISTINCT d.depart ) AS char ) AS deps,
FROM `0_rank` AS l
LEFT JOIN `0_right` AS r ON r.rank_id = l.id and r.user_id = $user_id
LEFT JOIN `0_member` AS m ON r.user_id = m.id
LEFT JOIN `0_depart` AS d ON m.id = d.user_id
WHERE r.section = $section_id
AND l.rank = '$rank_name'
GROUP BY r.user_id ORDER BY prenom, nom
Let me know if this works.
Try this:
(By converting the functionality of the IN (SELECT...) to an inner join, you get exactly the same results but it might be the optimizer will make better choices.)
SELECT r.user_id AS ID, m.prenom, m.nom
FROM `0_rank` AS l
LEFT JOIN `0_right` AS r ON r.rank_id = l.id and r.section_id = 2
LEFT JOIN `0_user` AS m ON r.user_id = m.id
INNER JOIN `0_depart` AS x ON l.section_id = x.section_id and x.user_id = $user_id AND x.section_id = $section_id
WHERE l.rank = 'mod'
GROUP BY r.user_id
I also moved the constraints on 0_right to the join statement because I think that is clearer -- presumably this change won't matter to the optimizer.
I know nothing about your DB structure but your subselect looks like it can be replaced with a simple INNER JOIN against whatever table has the depart column. MySQL is well known for its poor subquery optimization.
Without knowing the structures or indexes, I would first add "STRAIGHT_JOIN" if the critical criteria is in-fact from the 0-rank table. Then, ensure 0_rank has an index on "rank". Next, ensure the 0_right has an index on rank_id at a minimum, but rank_id, section to take advantage of BOTH your criteria. Index on 0_member on id.
Additionally, do you mean left-join (ie: record only required in the 0_rank or 0_member) on the respective 0_right and 0_member tables instead of a normal join (where BOTH tables must match on their IDs).
Finally, ensure index on the depart table on user_id.
SELECT STRAIGHT_JOIN
r.user_id AS ID,
m.prenom,
m.nom
FROM
0_rank AS l
LEFT JOIN `0_right` AS r
ON l.id = r.rank_id
AND r.section = 2
LEFT JOIN `0_member` AS m
ON r.user_id = m.id
WHERE
l.rank = 'mod'
AND depart IN (SELECT depart
FROM 0_depart
WHERE user_id = 2
AND user_sec = 2)
GROUP BY
r.user_id
---- revised post from feedback.
From the parameters you are listing, you are always including the User ID... If so, I would completely restructure it to get whatever info is for that user. Each user should apparently can be associated to multiple departments and may or may NOT match the given rank / department / section you are looking for... I would START the query with the ONE USER because THAT will guarantee a single entry, THEN tune-down to the other elements...
select STRAIGHT_JOIN
u.id,
u.prenom,
u.nom,
u.other_stuffs,
rank.rank_name
from
0_user u
left join 0_right r
on u.id = r.user_id
AND r.section_id = $section_id
join 0_rank rank
on r.rank_id = rank.id
AND rank.rank_name = '$rank_name'
left join 0_dept dept
on u.id = dept.user_id
where
u.id = $user_id
Additionally, I have concern about your table relationships and don't see a legit join to the department table...
0_user
0_right by User_ID
0_rank by right.rank_id
0_dept has section which could join to rank or right, but nothing to user_id directly
Run explain on the query - it will help you find where the caveats are:
EXPLAIN SELECT r.user_id AS ID, m.prenom, m.nom
FROM 0_rank AS l
LEFT JOIN `0_right` AS r ON r.rank_id = l.id
LEFT JOIN `0_member` AS m ON r.user_id = m.id
WHERE r.section = 2
AND l.rank = 'mod' AND depart IN
(SELECT depart FROM 0_depart WHERE user_id = 2 AND user_sec = 2)
GROUP BY r.user_id\G