How to run nested left join in mysql - mysql

I have three tables, likes, users and statuses. So I am returning from likes tables and joining likes table with user table to see who liked it and joining likes table with statuses table to see who posted it.
Now want to get the user information from status table to see who wrote the status. Here comes the problem.
SQL Fiddle http://sqlfiddle.com/#!9/d0707b/2
My current query
select l.*, s.* , a.id as aid, a.userName from likes l
left join
statuses s on l.source_id = s.id
left join
users a on l.user_id = a.id
where
l.user_id in (5,7)
or
(s.privacy='Public' and s.interest in ('mobile', 'andriod') )
order by l.id desc
Here s.user_id=a.id I want to join the statuses table with user table.
[If question is not clear please comment, will try to edit]
Thank you.

You have to make a join to the user table again. Take a look here:
SELECT
l.*, s.*,
a.id AS aid,
a.userName,
b.userName
FROM
likes l
LEFT JOIN statuses s ON l.source_id = s.id
LEFT JOIN users a ON l.user_id = a.id
LEFT JOIN users b ON s.user_id = b.id
WHERE
l.user_id IN (5, 7)
OR (
s.privacy = 'Public'
AND s.interest IN ('mobile', 'andriod')
)
ORDER BY
l.id DESC

Related

MYSQL: Handling Multiple LEFT JOINS

I have a query with one LEFT JOIN that works fine. When I add a second LEFT JOIN to a table with multiple records per field in the first table, however, I am getting the product of the results in the two tables ie books x publishers returned. How can I prevent this from happening?
SELECT a.*,b.*,p.*, group_concat(b.id as `bids`)
FROM authors `a`
LEFT JOIN books `b`
ON b.authorid = a.id
LEFT JOIN publishers `p`
on p.authorid = a.id
GROUP by a.id
EDIT:
Figured it out. The way to do this is to use subqueries as in this answer:
SELECT u.id
, u.account_balance
, g.grocery_visits
, f.fishmarket_visits
FROM users u
LEFT JOIN (
SELECT user_id, count(*) AS grocery_visits
FROM grocery
GROUP BY user_id
) g ON g.user_id = u.id
LEFT JOIN (
SELECT user_id, count(*) AS fishmarket_visits
FROM fishmarket
GROUP BY user_id
) f ON f.user_id = u.id
ORDER BY u.id;
If you do multiple LEFT Joins, your query will return a cartesian product of the results. To avoid this and get only one copy of fields you desire, do a subquery for each table you wish to join as below. Hope this helps someone in the future.
SELECT u.id
, u.account_balance
, g.grocery_visits
, f.fishmarket_visits
FROM users u
LEFT JOIN (
SELECT user_id, count(*) AS grocery_visits
FROM grocery
GROUP BY user_id
) g ON g.user_id = u.id
LEFT JOIN (
SELECT user_id, count(*) AS fishmarket_visits
FROM fishmarket
GROUP BY user_id
) f ON f.user_id = u.id
ORDER BY u.id;

delete results of a select with a JOIN

I have a table of users that may or may not have photos uploaded. I want to delete any users that don't have photos. Right now I can get the users with no photos like this:
SELECT a.id FROM users a
LEFT JOIN images b
ON a.id = b.user_id
WHERE b.user_id is null
However, I'm a little stumped on how to actually make the delete happen.
This doesn't work:
DELETE FROM users c WHERE c.id IN ( SELECT a.id FROM users a
LEFT JOIN images b
ON a.id = b.user_id
WHERE b.user_id is null
)
And neither does this:
DELETE FROM users a
LEFT JOIN images b
ON a.id = b.user_id
WHERE b.user_id is null
What's the right way to do a delete on the results of a JOIN?
I am guessing your 2nd example failing because it is referencing the table in the subquery, but it shouldn't actually need to:
DELETE FROM users c WHERE c.id NOT IN ( SELECT DISTINCT user_id FROM images);
Otherwise, I would've done this:
DELETE FROM users
USING users AS u LEFT JOIN images AS i ON u.id = i.user_id
WHERE i.user_id IS NULL;
DELETE users
WHERE NOT EXISTS (
SELECT 1
FROM images i
WHERE users.user_id = i.user_id)
You are almost there with the delete with joins query but need to give the table from which to delete.
Try this:
delete a.* from users a left join images b on a.user_id=b.user_id where b.user_id is null;

MySQL check if value is in result set before summing

I have a table of ratings for comments, when I fetch comments, I also fetch the ratings and I also want to be able to display which comments the logged user has already voted on. This is what I am doing now
SELECT
c.id,
c.text,
c.datetime,
c.author,
u.email AS author_name,
SUM(cr.vote) AS rating,
cr2.vote AS voted
FROM comments c
LEFT JOIN users u ON u.id = c.author
LEFT JOIN comments_ratings cr ON c.id = cr.comment
LEFT JOIN comments_ratings cr2 ON c.id = cr2.comment AND cr2.user = :logged_user_id
GROUP BY c.id ORDER BY c.id DESC
But I don't like how I'm performing a second join on the same table. I know it is perfectly valid but if I could get the information I want from the first join, which is there anyway, why perform a second one?
Is it possible to figure out if a row with column user equal to :logged_user_id exists on table comments_ratings cr before executing the aggregate function(s)?
P.S.: If someone could come up with a better title, people can find in future, I'd also appreciate that.
You can do what you want with conditional aggregation:
SELECT c.id, c.text, c.datetime, c.author, u.email AS author_name,
SUM(cr.vote) AS rating,
MAX(cr.user = :logged_user_id) as voted
FROM comments c LEFT JOIN
users u
ON u.id = c.author LEFT JOIN
comments_ratings cr
ON c.id = cr.comment
GROUP BY c.id
ORDER BY c.id DESC;

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

chat application friend list

I have 3 MySQL tables namely chat_comments, chat_friends and user_details and I want to display a friend list.
My tables:
chat_comments(comment_id,comment,user_id,user_id2,date_added)
chat_friends(user_id,user_id2,approved)
user_details(user_id, mainimage_id, fullname)
To do this, I need a query that will return the needed fields (u.mainimage_id, u.fullname, b.comment, b.user_id) so I can loop through the list to display a table.
SQL so far (help from #Andriy M):
SELECT
cc.comment,
cc.date_added,
u.fullname,
u.mainimage_id
FROM
user_details u
LEFT JOIN
chat_comments cc
INNER JOIN (
SELECT
user_id,
MAX(comment_id) AS maxcomment
FROM chat_comments WHERE user_id=2020 OR user_id2=2020
GROUP BY user_id
) a ON a.user_id = cc.user_id
AND a.maxcomment = cc.comment_id
ON a.user_id = u.user_id
WHERE u.user_id IN (
SELECT user_id2
FROM chat_friends
WHERE user_id = 2020
AND approved = 1
)
The above query returns the last comment made by the logged-in user's friends in conversation not the last comment between the logged-in user and his/her friend regardless of who made it.
I would like it to return the last comment between the logged-in user and their friend individually regardless of who made it. In the chat_messages table, user_id is the sender and user_id2 is the receiver. Hope it makes sense?
Like #imm said in a comment, you need to use an outer join. In case of a left join, the user_details table should become the left side of the join, the right side being the result of your inner join of chat_comments with your a derived table. You'll also need to remove the user_id IN (…) condition from inside the a subselect and re-apply it to the user_details table. Here:
SELECT
cc.comment,
cc.date_added,
u.fullname,
u.mainimage_id
FROM
user_details u
LEFT JOIN
chat_comments cc
INNER JOIN (
SELECT
user_id,
MAX(comment_id) AS maxcomment
FROM chat_comments
GROUP BY user_id
) a ON a.user_id = cc.user_id
AND a.maxcomment = cc.comment_id
ON a.user_id = u.user_id
WHERE u.user_id IN (
SELECT user_id2
FROM chat_friends
WHERE user_id = 2020
AND approved = 1
)
;
Alternatively, you could use a right join. In this case you would just need to move the user_id IN (…) condition, similarly to the LEFT JOIN solution above, and replace the second INNER JOIN with RIGHT JOIN:
SELECT
cc.comment, cc.date_added, u.fullname, u.mainimage_id
FROM
(
SELECT user_id, MAX(comment_id) AS maxcomment
FROM chat_comments
GROUP BY user_id
) a
INNER JOIN
chat_comments cc ON
a.user_id = cc.user_id AND
a.maxcomment = cc.comment_id
RIGHT JOIN
user_details u ON
a.user_id = u.user_id
WHERE u.user_id IN (select user_id2 from chat_friends where user_id=2020 AND approved=1)