Joining 3 tables, not getting desired result - mysql

I have three tables in MySql:
Events, Users both having many to many relation. Hence, third table Attend.
Table content:
Events:
e_id, e_content
Users
u_id, u_details
Attend
e_id, u_id, attending
let consider I am logged in and my uid is 1005.
So I want to see all events whether or not I am attanding the evnet but if I am attending the Attend.attending column should be yes else it should be null
I have tried a lot with joins but I have not received the desired query.
like:
select e.e_id,u.u_details,a.attending
from Events e
left join attend a on e.e_id = a.e_id
left join users u on u.u_id = a.U_id
and u.u_id = 1005;
with the above query I get same result for all uid

You need to do a LEFT JOIN here like
select e.e_id,u.u_details,a.attending
from Events e
left join attend a on e.e_id = a.e_id
left join users u on u.u_id = a.U_id
and u.u_id = 1005;

If, as is specifically asked, all events require displaying, but just where a particular user (1005) is attending has attending = "Yes" then is requires a little more than left joins, as if multiple users are attending, it will still display "Yes" for all users attending, even though the u_id is not displayed.
http://sqlfiddle.com/#!9/ed738/3/1
Shows the query with 2 left joins, and then the 2nd result set which gets what I believe is the required result.
select e.e_id,a.u_details,a.attending
from Events e
left join (select a.*, u.u_details
from attend a
INNER join users u ON
(a.u_id = u.u_id and u.u_details = 'will')) a on e.e_id = a.e_id
There are more than likely more elegant ways of doing this, but think this does the job.

Related

SELECT, 2 counts from 2nd table, RIGHT JOIN on 3rd

I'm trying to gather "followers" for a specific user (#1 in this code).
I'm doing my primary select from followers as the column following will have user #1 and followers.userid will have the userid of the person doing the following.
Next I'm trying to get a count of records from the experiences that have the user id of the follower (how many experiences does this follower have?)
Next, the follower will have rated each experience (1-5 stars) and I want to sum those ratings (experiences.stars) to get an average rating of all experiences.
Lastly, I want to join the followers user record from the users table.
I should end up with
userid, jobs, stars, * from users
SELECT * FROM followers AS F
RIGHT JOIN
(SELECT count(id) FROM experiences AS M WHERE M.userid = F.userid) AS jobs
RIGHT JOIN
(SELECT sum(stars) FROM experiences AS S WHERE S.userid = F.userid) AS stars
RIGHT JOIN
users AS U ON U.userid = F.userid
WHERE F.following = 1 /* #1 = the user # I want the follwers of/for */
I've also tried:
SELECT * FROM followers AS F,
(SELECT count(id) FROM experiences AS M WHERE M.userid = F.userid) AS jobs,
(SELECT sum(stars) FROM experiences AS S WHERE S.userid = F.userid) AS stars
RIGHT JOIN
users AS U ON U.userid = F.userid
WHERE F.following = 1 /* #1 = the user # I want the follwers of/for */
In cPanel, I'm getting an error that I have syntax error at WHERE F.userid in both statements.
A) what am I missing and B) is there a better way to do this?
It seems to me, the query would be easier to follow like so:
SELECT *
FROM followers AS F
LEFT JOIN users AS U ON U.userid = F.userid
LEFT JOIN (SELECT count(id) FROM experiences AS M WHERE M.userid = **F.userid)** AS jobs
LEFT JOIN (SELECT sum(stars) FROM experiences AS S WHERE S.userid = F.userid) AS stars
WHERE F.following = 1 /* #1 = the user # I want the follwers of/for */
;
All those RIGHT JOINs you originally had would only give you followers that had both "types" of experiences.
Also, correlated subqueries can be expensive (and you didn't need two of them...actually, you didn't even need subqueries), so I'd also rework it like so....
SELECT F.*, U.*, count(x.id), sum(x.stars)
FROM followers AS F
LEFT JOIN users AS U ON U.userid = F.userid
LEFT JOIN experiences AS x ON F.userid = x.user_id
WHERE F.following = 1
GROUP BY [all the fields selected in F and U, or just F.userid if server settings allow]
;
Seems like there's a couple of ON clauses missing.
I know that RIGHT outer joins are supported, but why would we write it that way, and not write it as LEFT outer joins. (We typically reserve RIGHT joins to the towers of academia.)
And it's well past time to ditch the old-school comma syntax for join operations. (Yes, it's still supported for backwards compatibility with existing statements. But new development should use the newer JOIN syntax.)
The condition requiring a non-NULL value of F.following would effectively negate the "outerness" of the join, rendering it equivalent to an INNER join. For clarity, we should either write that as an inner JOIN, or if we want an outer join, we should relocate that condition to the appropriate ON clause.
Also, best practice is to qualify all column references; even when they aren't ambiguous to the optimizer, it makes it easier on the future reader (so the future reader doesn't have to confirm which table contains the id column), as well as protecting the query from throwing "ambiguous column" errors in the future if a column named id is added to another table used by the query.
Also, it's not valid to reference columns from F in the outer query inside inline view queries. We can use a correlated subquery, but not as an inline view.
The specification isn't clear. Example data and sample of expected output would go a long ways to clarifying the requirements.
If we want to use correlated subqueries that return a single row, with a single column, we can put those in the SELECT list ...
SELECT f.*
, u.*
, ( SELECT COUNT(m.id)
FROM experiences m
WHERE m.userid = f.userid
) AS jobs
, ( SELECT SUM(s.stars)
FROM experiences s
WHERE s.userid = f.userid
) AS stars
FROM followers f
LEFT
JOIN users u
ON u.userid = f.userid
WHERE f.following = 1 /* #1 = the user # I want the follwers of/for */
ORDER BY ...
We could get an equivalent result using inline views, but that would look quite different.
I would tend to do the aggregation inside the inline view, something along the lines of this:
SELECT f.*
, u.*
, IFNULL(e.jobs,0) AS jobs
, IFNULL(e.stars,0) AS stars
FROM followers f
LEFT
JOIN users u
ON u.userid = f.userid
LEFT
JOIN ( SELECT ef.userid
, COUNT(ee.id) AS jobs
, SUM(ee.stars) AS stars
FROM followers ef
JOIN experiences ee
ON ee.userid = ef.userid
WHERE ef.following = 1 /* argument */
GROUP BY ef.userid
) e
ON e.userid = f.userid
WHERE f.following = 1 /* argument */
ORDER BY ...

Join two tables query

I have two tables users and linkage. I am creating a link between one user to another. Means user A will be linked to user B and vise versa.
Now I want to get the details of linked users for a particular entered user_id. Means If user A is finding his linked Id's then the details of linked id's should be seen.
Linkage has three columns its id, user_id and linked_contact_id.
Users has columns as user_id, user_name,pass etc..
I tried one join but I only get the linked Id's from this not the details of id's.
SELECT * FROM Users INNER JOIN linkage ON linkage.user_id = Users.user_id WHERE linkage.linked_contact_id = 1
output
user_id linked_contact_id
1 4
1 1
1 5
How can I get this?Please help.. Thank you...
You will need to join on the users table a second time:
SELECT u.*, u2.* FROM Users u
INNER JOIN linkage l ON l.user_id = u.user_id
INNER JOIN Users u2 ON l.linked_contact_id = u2.user_id
WHERE l.linked_contact_id = 1
Please note that as you are selecting the same columns twice (in u.*, u2.*), you will probably have to list out each field with an alias to distinguish between them.
Something wrong on your ratio. But it's okay.
Try LEFT JOIN or RIGHT JOIN
SELECT * users
LEFT JOIN linkage
ON linkage.user_id = users.user_id
WHERE linkage.linked_contact_id = 1
Get Users information
SELECT users.*,
linkage.*,
contact.user_id as contact_user_id,
contact.user_name AS contact_user_name
FROM users
INNER JOIN linkage ON linkage.user_id = users.user_id
INNER JOIN users AS contact ON linkage.linked_contact_id = contact.user_id
WHERE linkage.linked_contact_id = 1
Edit
Here Screenshot of Query Output

MySQL LEFT JOIN with WHERE returns NULL

Trying to JOIN a few tables to supply the user some information on other users he subscribed to this session.
There is a list of other users with their pictures
this works:
SELECT DISTINCT profile_picupload.imageFull, goals_list.goal, goal03.other,
users.username, users.email FROM profile_picupload
JOIN anal_muses
ON anal_muses.username = profile_picupload.username
JOIN goal03 ON goal03.username = anal_muses.username
JOIN goals_list ON goal03.goal=goals_list.id
JOIN users ON users.email = anal_muses.username
it gives me the desired list.
But - i need the list from anal_muses for this session only - so i changed the code to this:
SELECT DISTINCT profile_picupload.imageFull, goals_list.goal, goal03.other,
users.username, users.email FROM profile_picupload
right JOIN anal_muses
ON anal_muses.username = profile_picupload.username
AND anal_muses.sessionId='$sessionId'
JOIN goal03 ON goal03.username = anal_muses.username
JOIN goals_list ON goal03.goal=goals_list.id
JOIN users ON users.email = anal_muses.username
table profile_picupload - holds only one picture for each user
while table anal_musers holds many rows for each user ( depends on the number of people he subscribed to)
so when i added AND anal_muses.sessionId='$sessionId' I needed to change the first join to RIGHT JOIN
but now i get a list with NULL values for profile_picupload .imageFull
.....
is there a way around this?
Thank you :-)

Get users with followers, and without followers

I have a really simple table - follow - in which I store followers.
user | following
-----------------
1 | 2
The above means user 1 is following user 2.
I want to display all users on the home page and order them buy who has the most followers, and then return the rest of the users who have no followers. The below query is working as far as displaying the users, but I can't figure out how to retrieve the users who do not have any followers. I've tried RIGHT JOIN users u ON f.following=u.id but that gives me weird results.
This query returns user 2 who has a follower, but doesn't return users 1 and 3, who do not have followers.
Edit: this query is also checking to see if the user is following back, which is why I'm joining using the ID of 1 as a test.
SELECT
u.id
,u.username
,u.avatar
,COUNT(1) AS followers
,ul.*
,fo.*
FROM follow f
LEFT JOIN users u ON f.following=u.id
LEFT JOIN follow fo ON fo.following=u.id AND fo.user=1
LEFT JOIN users_likes ul ON ul.likes=u.id AND ul.user=1
GROUP BY f.following
ORDER BY COUNT(1) DESC
SQL Fiddle: http://sqlfiddle.com/#!2/98f65/1
The problem with your query in the question is that you are left-joining to the follow table. That means that all rows in the follow table are included regardless of their connection to another table. What you want is to show all users, so that is the table that should be on the outer end of the join.
I also think you're trying to do too many things at once here, which is why you're having trouble figuring it out. You want to know who has followers and who doesn't, who's following back, order them, consider the users_likes and so on. I recommend taking a step back and breaking them down into individual queries, and then building those into one result set as needed.
To get the users and number of followers, you can outer join the users table with the follow table like this:
SELECT u.id, u.username, u.avatar, (IFNULL(COUNT(f.following), 0)) AS numFollowers
FROM users u
LEFT JOIN follow f ON f.following = u.id
GROUP BY u.id
ORDER BY numfollowers DESC;
IFNULL is used to check the cases when there are no followers, and no link is made in the outer join so a null value appears.
If you want to work in the users_likes table, you should add it in as another left join. The problem this causes, is that it will return null values for all columns if there are no likes. (Example, if I left join the users_likes table here, I will see null for users 1 and 3 because nobody 'likes' them.) To make the result set a little more understandable, I recommend you don't collect all rows of the users_likes table. Perhaps this query would make more sense:
SELECT u.id, u.username, u.avatar, (IFNULL(COUNT(f.following), 0)) AS numFollowers, ul.user AS likedByUser, ul.created_at
FROM users u
LEFT JOIN follow f ON f.following = u.id
LEFT JOIN users_likes ul ON ul.likes = u.id
GROUP BY u.id
ORDER BY numfollowers DESC;
As far as whether or not a user is following back, I think this would change a bit, as the above only shows the number of followers, and doesn't produce a row for each follower.
Let me know if you have any more questions, here is an SQL Fiddle for the above. I will leave it up to you for handling the null values that occur right now.
You can use an outer join (left or right) from Users to your current query in any number of ways. An easy example that should get you started. This isn't a clean-up up solution, just a dmeo of a way that will work.
SELECT a.*
,b.*
FROM users a
LEFT JOIN (
SELECT
u.id
,u.username
,u.avatar
,COUNT(1) AS followers
FROM follow f
LEFT JOIN users u ON f.following=u.id
LEFT JOIN follow fo ON fo.following=u.id AND fo.user=1
LEFT JOIN users_likes ul ON ul.likes=u.id AND ul.user=1
GROUP BY f.following
) b
ON a.id = b.id
ORDER BY followers DESC
You can do this:
SELECT * FROM (
SELECT u.id, u.username, u.avatar, COUNT(f.user) as followers
FROM users AS u
LEFT JOIN follow AS f ON u.id = f.following
GROUP BY u.id
) AS subselect ORDER BY subselect.followers DESC

Basic many-to-many left join query

So I have 3 tables:
users
achievements
achievements_unlocked (many-to-many)
I would like a query to return all achievements available, plus mention what achievements the current user (id_user=123 for example) has unlocked.
How can I do this? This should involve a LEFT JOIN but if I add "WHERE id_user = 123" it won't get me achievements locked...
Thanks!
Try this
SELECT ac.*,
CASE WHEN un.ach_id IS NULL THEN 0
ELSE 1 END AS user_unlocked
FROM achievements ac
LEFT JOIN
(SELECT au.ach_id FROM achievements_unlocked au
INNER JOIN users u ON au.user_id = u.id
WHERE u.id = 123) un
ON ac.id = un.ach_id
Select *
From achievements
Left Outer Join achievements_unlocked On achievements_unlocked.achievement_id = achievements.achievement_id
Left Outer Join users On
users.user_id = achievements_unlocked.user_id And
users.user_id = 123
This will likely be the most efficient way to do this without having to do nested loops and hash matching in the execution plan. Make sure you have indexes on the achievement_id and user_id columns on all tables that contain them.