MySQL Column 'id' in IN/ALL/ANY subquery is ambiguous - mysql

I am trying to do a search functionalities that involves three tables.
Searching for users and returning wheather the user id 1 is a friend of the returned users. Also The returned users is being filtered from a third table where it checks tag of that users.
So I can say, "Return users who has tag 'Programming', 'Php'
in userinterests table and also if the returned user is a friend of usr id 1 or not "
I am trying to use the bellow query but getting Column 'id' in IN/ALL/ANY subquery is ambiguous
If I remove the left join then it works.
SELECT n.id, n.firstName, n.lastName, t.id, t.tag, t.user_id, if(id in (
SELECT u.id as id from friends f, users u
WHERE CASE
WHEN f.following_id=1
THEN f.follower_id = u.id
WHEN f.follower_id=1
THEN f.following_id = u.id
END
AND
f.status= 2
), "Yes", "No") as isFriend
FROM users n
LEFT JOIN userinterests t on n.id = t.id
WHERE t.tag in ('Programming', 'Php')
Thank you for your time :)

Qualify all your column names. You seem to know this, because all other column names are qualified.
I'm not sure if your logic is correct, but you can fix the error by qualifying the column name:
SELECT . . .
(CASE WHEN n.id IN (SELECT u.id as id
FROM friends f CROSS JOIN
users u
WHERE CASE WHEN f.following_id=1
THEN f.follower_id = u.id
WHEN f.follower_id=1
THEN f.following_id = u.id
END
) AND
f.status= 2
THEN 'Yes' ELSE 'No'
END) as isFriend
. . .

This is the way I will go for your approach:
1) I used INNER JOIN instead of LEFT JOIN for skip users that are not related to tags: Programming and Php.
2) I replaced the logic to find the set of friends related to user with id equal to 1.
SELECT
n.id,
n.firstName,
n.lastName,
t.id,
t.tag,
t.user_id,
IF(
n.id IN (SELECT follower_id FROM friends WHERE status = 2 AND following_id = 1
UNION
SELECT following_id FROM friends WHERE status = 2 AND follower_id = 1),
"Yes",
"No"
) AS isFriend
FROM
users n
INNER JOIN
userinterests t ON n.id = t.id AND t.tag IN ('Programming', 'Php')
Just curious, whats is the meaning of status = 2 ?

Related

Joining 3 Tables based on specific conditions

I have the following 3 tables:
users: [id, name, admin ...]
events: [id, user_id, type ...]
messages: [id, user_id, ...]
I want to construct a query that does the following:
-> Select all users from the table users who have not scheduled an event of the type "collection"
-> And who have less than 3 messages of the type "collection_reminder"
-> And who are not admin
I've managed to figure out the first part of this query, but it all goes a bit pear shaped when I try to add the 3 table, do the count, etc.
Here is a query that might get the job done. Each of the requirement is represented as a condition in the WHERE clause, using correlated subqueries when needed:
SELECT u.*
FROM users u
WHERE
NOT EXISTS (
SELECT 1
FROM events e
WHERE e.user_id = u.id AND e.type = 'collection'
)
AND (
SELECT COUNT(*)
FROM messages m
WHERE m.userid = u.id AND m.type = 'collection_reminder'
) <= 3
AND u.admin IS NULL
Ill try this on the top of the head so expect some synthax issues, but the idea is the following.
You can filter out who have no events schedule using a left join. On a left join the elements on the second part of the query will show up as null.
select * from users u
left join events e on e.user_id = u.id
where e.user_id is null
Now, i dont think this is the most performant way, but a simple way to search for everyone that has 3 or less messages:
select * from users u
left join events e on e.user_id = u.id
where u.id in (
select COUNT(*) from messages m where m.user_id = u.id HAVING COUNT(*)>3;
)
and e.user_id is null
Then filtering who is not admin is the easiest :D
select * from users u
left join events e on e.user_id = u.id
where u.id in (
select COUNT(*) from messages m where m.user_id = u.id HAVING COUNT(*)>3;
)
and e.user_id is null
and u.admin = false
Hope it helps.
This is pretty much a direct translation of your requirements, in the order you listed them:
SELECT u.*
FROM users AS u
WHERE u.user_id NOT IN (SELECT user_id FROM events WHERE event_type = 'Collection')
AND u.user_id IN (
SELECT user_id
FROM messages
WHERE msg_type = 'Collection Reminder'
GROUP BY user_id
HAVING COUNT(*) < 3
)
AND u.admin = 0
or alternatively, this can be accomplished completely with joins:
SELECT u.*
FROM users AS u
LEFT JOIN events AS e ON u.user_id = e.user_id AND e.event_type = 'Collection'
LEFT JOIN messages AS m ON u.user_id = m.user_id AND m.msg_type = 'Collection Reminder'
WHERE u.admin = 0
AND e.event_id IS NULL -- No event of type collection
GROUP BY u.user_id -- Note: you should group on all selected fields, and
-- some configuration of MySQL will require you do so.
HAVING COUNT(DISTINCT m.message_id) < 3 -- Less than 3 collection reminder messages
-- distinct is optional, but
-- if you were to remove the "no event" condition,
-- multiple events could multiply the message count.
;
This query uses joins to link the 3 tables, filters the result using the where clause, and using Group by, having limiting the result to only those who satisfy the less than count condition..
SELECT a.id,
SUM(CASE WHEN b.type = 'collection' THEN 1 ELSE 0 END),
SUM(CASE WHEN c.type = 'collection_reminder' THEN 1 ELSE 0 END
FROM users a
left join events b on (b.user_id = a.id)
left join messages c on (c.user_id = a.id)
WHERE a.admin = false
GROUP BY a.id
HAVING SUM(CASE WHEN b.type = 'collection' THEN 1 ELSE 0 END) = 0
AND SUM(CASE WHEN c.type = 'collection_reminder' THEN 1 ELSE 0 END) < 3

Return multiple data with condition from multiple tables in mysql

I am having problem with fetching data from multiple tables with some conditions in MySQL.
I have follwing three tables:
Like Table
Like_id photoID userID
1 1 1
2 2 2
3 2 1
BookMark Table
bookmark_id photoID userID
1 1 1
2 2 2
3 2 1
Users Table
User_id Name Email
1 Max B maxb#gmailcom
2 Tom Smith toms#gmailcom
CONDITIONS:
At first i want to check whether there is any data from the LIKE table for the userID = 2. If there is no data it should return "false" otherwise it should return "true".
Similarly, i want to check whether there is any data from the BOOKMARK table for the userID = 2. If there is no data it should return "false" otherwise it should return "true".
Finally, i want to fetch the Name and Email from the USERS table for the userID = 2.
WANTED:
I want to achieve all these information in a SINGLE QUERY with the above mentioned conditions from these three tables.
SO FAR tried with this QUERY:
select Like.Like_id from (Like left join Users on Like.userID = Users.User_id)
left join BookMark on Users.User_id = BookMark.bookmark_id
where Users.User_id = 2
With #Gervs suggestion:
SELECT
u.user_id,
u.name,
u.email,
(CASE WHEN ISNULL(l.user_id) THEN 'false' ELSE 'true' END) AS 'likes',
(CASE WHEN ISNULL(b.user_id) THEN 'false' ELSE 'true' END) AS 'bookmarks'
FROM
users u
LEFT JOIN
likes l
ON u.user_id = l.user_id
LEFT JOIN
bookmarks
ON u.user_id = b.user_id
WHERE u.user_id = 2
GROUP BY u.user_id
What will be the easiest but efficient single query to fetch these information?
Will VIEW be a best option for these conditions?
Advanced thanks for your participation.
You can inner join both like table and bookmark table on users table, that is if you want only users that have entries in both tables.
SELECT
u.user_id,
u.name,
u.email,
COUNT(l.user_id) likes,
COUNT(b.user_id) bookmarks
FROM
users u
JOIN
likes l
ON u.user_id = l.user_id
JOIN
bookmarks b
ON u.user_id = b.user_id
WHERE u.user_id = 2
GROUP BY u.user_id
If you always want the user, just change the inner joins into left joins and likes and/or bookmarks will be zero if no entries are found
SELECT
u.user_id,
u.name,
u.email,
CASE WHEN COUNT(l.user_id) > 0 THEN 'true' ELSE 'false' END likes,
CASE WHEN COUNT(b.user_id) > 0 THEN 'true' ELSE 'false' END bookmarks
FROM
users u
LEFT JOIN
likes l
ON u.user_id = l.user_id
LEFT JOIN
bookmarks b
ON u.user_id = b.user_id
WHERE u.user_id = 2
GROUP BY u.user_id

Creating a new column in an SQL query

I have one table to store users and one table to store member fee payments:
Users
Id (int)
Name (string)
MemberFeePayments
Id (int)
User (int)
Year (int)
The "User" column in MemberFeePayment is a reference (foreign key) to the user that have payed the fee. The "Year" column tells which year the user have payed the fee for. If a user has been a member for several years, there will be multiple rows for the user in the MemberFeePayments table.
Now I would like to write an sql query that fetches ALL users. The resulting table should have a column that tells if the users are members in a specific year (which will be provided to the query):
Result
Id (int)
Name (string)
IsMember (bool)
What is a good way to accomplish this?
I'm using MySql (5.6).
This should work if there is only one Fee Payment record per year:
SELECT
U.Id, U.Name,
CASE WHEN M.ID IS NULL THEN 0 ELSE 1 END AS `IsMember`
FROM Users U
LEFT JOIN MemberFeePayments M ON (U.Id = M.User AND M.Year = 2013)
This will work if you have multiple payments in year:
SELECT
U.Id, U.Name,
CASE WHEN COUNT(M.ID) > 0 THEN 1 ELSE 0 END AS `IsMember`
FROM Users U
LEFT JOIN MemberFeePayments M ON (U.Id = M.User AND M.Year = 2013)
GROUP BY U.Id, U.Name;
Here's a link to a demo
Try this:
SELECT a.Id,a.Name,if(b.id>0,TRUE,FALSE) as ismember FROM USERS a LEFT JOIN MemberFeepayments b ON a.id=b.id
check column fields and table name
Here is a way you can do it. So it will lit all the users no matter they are in the MemberFeePayments table or not and will show Yes if they are member for a particular year else No.
select
x.Id,
x.Name,
case when x.member is not null then 'Yes' else 'No' end as IsMember
from
(
select
u.Id,
u.Name,
m.User as member
from Users u
left join MemberFeePayments m on m.User = u.Id AND m.year = 2014
)x
OR simply as below without doing the outer select from derived table.
select
u.Id,
u.Name,
case when m.User IS NOT NULL THEN 'Yes' else 'No' end as IsMember
from Users u
left join MemberFeePayments m on m.User = u.Id AND m.year = 2014
DEMO
Use left join
SELECT t1.id,t1.Name,t2.id
FROM Users t1
LEFT JOIN MemberFeePayments t2 ON t1.id = t2.user and t2.year = 2014
t2.id will be null if there are no data in MemberFeePayments for that user and year

Mysql join from multiple tables

I have 3 tables
friends
posts
members
friends
========
id, to, from (user_id's), status
there are 3 status's -1 = denied, 0 = no response/new, 1 = accepted
posts
=======
p_id, user_id (same as member_id), text
members
========
member_id, f_name, l_name
If like to select the text from the post in 'posts' combine it with the users name from 'members' and only display posts where the user_id is in the 'friends' table.
I would like to know if it can be done, I've tried an IN () statement in my query which worked, but it creates a new problem with generating the csv inside the IN (). I'd perfer to do this through mysql, but if it can't be done I may use a global variable to store friend data (but then it will not be upto date or will have to be refreshed when a user gets a new friend).
As I understand it, you want to find the name and posts of all your friends, not any friend that's in the friend table at all...?
Your own user id being in $myId, this should do it (newest posts first);
EDIT: Added status check for friends
SELECT m.f_name, m.l_name, p.`text`
FROM members m
JOIN posts p
ON m.member_id = p.user_id
JOIN friends f
ON f.`to` = m.member_id OR f.`from` = m.member_id
WHERE (f.`from` = $myId OR f.`to`= $myId)
AND f.`status` = 1 AND m.member_id <> $myId
ORDER BY p.p_id DESC
Try this :
SELECT p.text,m.f_name,m.l_name FROM posts p
LEFT OUTER JOIN members m ON p.user_id=m.member_id
where p.user_id in(select id from friends);
OR
SELECT p.text,m.f_name,m.l_name FROM posts p
LEFT OUTER JOIN members m ON p.user_id=m.member_id
INNER JOIN friends f on p.user_id=f.id
If I understand correctly, you have a user_id and you want all the posts authored by "friends" of that user. This query starts at posts, joins that to friends (where the author is the "destination" of the friendship) (at which point the WHERE clause will filter out any non-friend posts), and then joins in members to fill out the author's name info.
SELECT
posts.p_id
posts.text,
CONCAT(members.f_name, " ", members.l_name)
FROM
posts
JOIN friends ON posts.user_id = friends.to
JOIN members ON posts.user_id = members.member_id
WHERE
friends.from = ?
GROUP BY posts.p_id
I added a subquery to get all your friends since I assumed that if you have these records
Friends
==================================
ID TO FROM STATUS
==================================
1 1 2 1
2 3 1 1
and your member_id = 1, your friends are 2, 3. right?
SELECT b.f_name,
b.L_name,
c.`text`
FROM
(
SELECT `to` friendID
FROM friends
WHERE `status` = 1 AND
`from` = ? -- change this to your ID
UNION
SELECT `from` friendID
FROM friends
WHERE `status` = 1 AND
`to` = ? -- change this to your ID
) a INNER JOIN members b
ON a.friendID = b.member_ID
LEFT JOIN posts c
ON a.friendID = c.user_id

Select the users are not friend of a specific user

Hi, I have these two tables: users and friends (friend_status = 1 means the request is sent, friend_status = 2 means they are friends). Now I want to select all users are not friend of a specific user. How to do?
Assuming the current user is 1. I tried this SQL. It works but it's too long and slow. The first selects all users sent request to user1 but not accepted. The second selects all users receive request from user1. The third and the fourth selects all users is not in "friends" table.
SELECT user_id, name, email
FROM
(
SELECT user_id, name, email
FROM users u INNER JOIN friends f ON u.user_id = f.sender
WHERE f.receiver = 1 AND friend_status <> 2
UNION
SELECT user_id, name, email
FROM users u INNER JOIN friends f ON u.user_id = f.receiver
WHERE f.sender = 1 AND friend_status <> 2
UNION
SELECT u.user_id, u.name, u.email
FROM users u LEFT JOIN friends f ON u.user_id = f.sender
WHERE f.receiver IS NULL
GROUP BY user_id
UNION
SELECT u.user_id, u.name, u.email
FROM users u LEFT JOIN friends f ON u.user_id = f.receiver
WHERE f.sender IS NULL
GROUP BY user_id
) T
GROUP BY user_id
Update: Add a pic.
SELECT
a.user_id,
a.name,
a.email,
b.status IS NOT NULL AS friend_status
FROM
users a
LEFT JOIN
friends b ON
a.user_id IN (b.sender, b.receiver) AND
1 IN (b.sender, b.receiver)
WHERE
(b.friend_id IS NULL OR b.status <> 2) AND
a.user_id <> 1
You had asked a question previously here - "Select users who aren't friends with anyone", and I provided an answer which utilized a LEFT JOIN.
Building off of that, to select users who aren't friends with a specific user, we just simply need to add that specific user's ID to the LEFT JOIN condition (1 IN (b.sender, b.receiver).
Minor Edit: Unless the user can friend him/herself, it wouldn't make sense to also select the user who we're selecting against!! So I added WHERE a.user_id <> 1.
Assuming you want to perform the query on user_id 1:
SELECT user_id, name, email
FROM users AS u
WHERE NOT EXISTS (SELECT * FROM friends AS f
WHERE (f.sender = u.user_id AND f.receiver = 1 AND f.friend_status = 2)
OR (f.sender = 1 AND f.receiver = u.user_id AND f.friend_status = 2)
)
AND u.user_id <> 1
The subquery fetches all the established friendship relationship in which user 1 is either the sender or the receiver. The outer query selects all users for which no such relationship exists. The user with ID 1 is excluded from the query using the last line, as, even if he cannot be friend with himself, I suppose that he should not appear in the final query result.
You may be able to simplify this by using something like this:
SELECT user_id, name, email
FROM
(
SELECT u.user_id, u.name, u.email
FROM users u LEFT JOIN friends f ON u.user_id = f.sender
WHERE IFNULL(friend_status,0) <> 2
GROUP BY user_id
UNION
SELECT u.user_id, u.name, u.email
FROM users u LEFT JOIN friends f ON u.user_id = f.receiver
WHERE IFNULL(friend_status,0) <> 2
GROUP BY user_id
) T
GROUP BY user_id
The IFNULL function returns the value of the first parameter, replacing NULLs with the value of the value second parameter. In this case it means that friend_status will be treated as 0 if there is no matching friend in the friends table, which allows you to reduce the number of selects in the UNION by half.
Try this query
select
u.user_id,
u.name,
u.email,
ifnull(f.friend_status,0) as Relation
from users as u
left join friends as f
on f.sender = u.user_id
where u.user_id not in(select
sender
from friends
where sender = 1)
Here sender = 1 means the user id = 1. You can pass user id to restrict this condition. Also status 0 means he is not friend. and 1 , 2 , 3 are according to your rules