Problem using mysql joins - mysql

I'm fairly new to mysql and I have no idea if I'm heading in the right direction but I'm having trouble with a mysql query.
I basically have a table of users
id name
---- --------
1 user1
2 user2
3 user3
4 user4
as well as a table of user attributes
id userid attribute
---- ----- ------
1 1 5
2 1 6
3 2 5
4 3 4
I want to be able to select users that have both the attribute 5 and the attribute 6, so in this case I want to return
id name
---- --------
1 user1
I tried using a join like this.
SELECT u.id, u.name FROM users u LEFT JOIN attributes a ON (a.userid = u.id) WHERE a.attribute = 5 AND a.attribute = 6
But obviously that won't work, what is the best way of doing this?

One way to do this would be to use two joins; eg:
SELECT ...
FROM users u
JOIN attributes a5 ON u.id = a5.userid AND a5.attribute = 5
JOIN attributes a6 ON u.id = a6.userid AND a6.attribute = 6
Another way is by grouping (note that I am a MS SQL person, not sure if this is the right syntax for mysql or not):
SELECT u.id, u.name
FROM users u
JOIN attributes a ON u.id = a.userid
WHERE a.attribute IN (5,6)
GROUP BY u.id, u.name
HAVING COUNT(*) = 2

SELECT u.id, u.name FROM users u
INNER JOIN attributes a1 ON u.id = a1.userid
INNER JOIN attributes a2 ON u.id = a2.userid
WHERE a1.attribute = 5 AND a2.attribute = 6

Based on your question, I don't think the other two current answers are satisfactory.
If you want to:
select users that have both the
attribute 5 and the attribute 6
The following query is one way to accomplish that:
select
*
from
users
where
id in (select userid from attributes where attribute = 5)
and
id in (select userid from attributes where attribute = 6)

Hmm, I am not much into SQL, maybe GROUP BY and HAVING will help you:)
Check out the reference: http://www.w3schools.com/sql/sql_having.asp

Changing the query like this would work:
Select all from attributes table that equals 5 or 6 and then check the users who match.
SELECT a.id, u.name
FROM attributes AS a
LEFT JOIN users AS u ON a.id = u.id
WHERE a.attribute = 5 OR a.attribute = 6

Related

How to fix Sql query

Please i have been trying to get a query to return the friends a user has,in my members table, i have user id ,firstname and lastname of the member, i have user id column and friend id column in the friends table, now i am combining my members table with friends table so i can get the name of a friends.
users table
user_id firstname lastname
2 John drake
3 Hamer Joy
4 Finter Richy
friends table
friends_id user_id friend_id
1 2 3
2 4 2
3 4 3
here is the query i executed
SELECT a.friends_id,a.user_id,
a.friend_id, b.firstname, b.lastname
FROM friends AS a,users As b
WHERE (a.friend_id = b.user_id OR a.user_id = b.user_id) AND
(a.friend_id = 2 OR a.user_id =2)
here is the result i am getting
friends_id user_id friend_id firstname lastname
1 2 3 John drake
1 2 3 Hamer Joy
3 4 2 John drake
3 4 2 Finter Richy
This is the result i am expecting
friends_id user_id friend_id firstname lastname
1 2 3 Hamer Joy
3 4 2 Finter Richy
perhaps take a union of the specified individual's friends plus the users who have him as a friend:
select a.friends_id, a.user_id, a.friend_id, b.firstname, b.lastname
from friends a, users b
where a.user_id = 2 and b.user_id = a.friend_id
union
select a.friends_id, a.user_id, a.friend_id, b.firstname, b.lastname
from friends a, users b
where a.friend_id = 2 and b.user_id = a.user_id
This is all a single query resulting from the union of 2 inner queries.
I think the problem with your original query is that you are using an OR condition in your joins. Normally OR's in join conditions are not helpful.
Here is another solution that is a bit shorter and uses ANSI join syntax so you can clearly see the projections. Disclaimer, I haven't tested it in a database, so there may be an error. However, you can also access the original user name as well if you like (I've added that as the last two columns).
select F.friends_id, U.user_id, FR.user_id,
FR.firstname, FR.lastname,
U.firstname, U.lastname
FROM users U join Friends F on
U.user_id = F.user_id
JOIN Users FR on
F.friend_id = FR.user_id
where U.user_id = 2 or FR.user_id = 2
;
As a general rule, using the ANSI join syntax makes your query much easier to read and helps to ensure all of the join conditions are specified. It also gives you easy access to more join types.

Select with Multiple Counts on Left Join on Same Table

I'm not certain that this can be done, I have a table of users with a related table of user activity joined on a foreign key. Activity has different types, e.g. comment, like etc. I need to get users filtered by the number of each different type of activity.
What I have so far is this:
SELECT
users.*,
COUNT(t1.id) AS comments,
COUNT(t2.id) AS likes
FROM users
LEFT JOIN activity AS t1 ON users.id = t1.user_id
LEFT JOIN activity AS t2 ON users.id = t2.user_id
WHERE t1.activity_type_id = 1 AND t2.activity_type_id = 2
GROUP BY users.id
HAVING comments >= 5 AND likes >= 5
This seems to be close but it's returning a user with a count of 5 both likes and comments, when in reality the user has 5 likes and 1 comment.
To be clear I want this query to return users who have 5 or more likes and also users who have 5 or more comments.
UPDATE:
I've created an SQL Fiddle. In this case I have 3 users:
User 1: 6 comments, 8 likes
User 2: 3 comments, 2 likes
User 3: 5 comments, 2 likes
I want the query to return only user 1 and 3, with their respective totals of likes and comments.
http://sqlfiddle.com/#!2/dcc63/4
You can use conditional summing to do the count and due to the way MySQL treats boolean expressions an expression like sum(case when et.name = 'comment' then 1 else 0 end) (the "normal" SQL syntax) can be reduced to sum(case when et.name = 'comment').
SELECT
u.id,
sum(et.name = 'comment') AS comments,
sum(et.name = 'like') AS likes
FROM users AS u
LEFT JOIN engagements AS e ON u.id = e.user_id
JOIN engagement_types AS et ON e.engagement_type_id = et.id
GROUP BY u.id
HAVING sum(et.name = 'comment') >= 5
OR sum(et.name = 'like') >= 5
Result:
| ID | COMMENTS | LIKES |
|----|----------|-------|
| 1 | 6 | 8 |
| 3 | 5 | 2 |
Sample SQL Fiddle

Join and multiple and conditions

I have users table
ID NAME
1 John
2 Mike
3 Jack
and table with attributes and user IDs
USER ATTRIBUTE
1 1
1 2
2 4
I need to select all users with attribute 1 AND 2 (so, in this example user #1 John). Attributes can be more than two.
I'v tried
SELECT * FROM user u LEFT JOIN attributes a ON u.id = a.user
WHERE a.attribute = 1 AND a.attribute = 2
but of course it not working..
You will need to use a combination of IN() and GROUP BY ... HAVING to achieve this. Also no need for a join if all you need is user ID's. So something like:
SELECT user, COUNT(attribute) AS attribute_count
FROM attributes
WHERE attribute IN(...) /* include your set of attributes here */
GROUP BY user
HAVING attribute_count = ? /* include number equal to number of attribute ID's in IN() above */
If you need user id's and names you can simply join this record set derived from the query above as a filter to the users table:
SELECT user.id, user.name
FROM user
INNER JOIN
(
SELECT user, COUNT(attribute) AS attribute_count
FROM attributes
WHERE attribute IN(...) /* include your set of attributes here */
GROUP BY user
HAVING attribute_count = ? /* include number equal to number of attribute ID's in IN() above */
) AS filter
ON user.id = filter.user
having clause can be used with sum
SELECT u.id FROM user u
INNER JOIN attributes a ON u.id = a.user
group by u.id
having ( sum(case when attribute in (1,2) then 1 else 0 end) ) =2
You are looking for all users for whom EXIST attribute 1 and 2. One way to solve this - as the name suggests - is the EXISTS clause:
select *
from users u
where exists
(
select *
from user_attributes ua
where ua.user = u.id
and ua.attribute = 1
)
and exists
(
select *
from user_attributes ua
where ua.user = u.id
and ua.attribute = 2
);
Another way is this: Find all user ids that have both attributes, then select from users table.
select *
from users
where id in
(
select user
from user_attributes
where attribute in (1,2)
group by user
having count(*) = 2
);
In case there are duplicate entries in attributes, you would have to replace count(*) with count(distinct attribute).
There are other ways to approach this. I think these two mentioned are rather straight-forward.
You'll need a group by .... having
SELECT u.name
FROM
users u
JOIN
attributes a
ON u.id = a.user
WHERE a.id IN (1,2)
GROUP BY u.name
HAVING COUNT(*) = 2

Using select statement with two tables

I have two tables. One contains User and company relationship a show below
User_company
UserId CompanyId
1 2
2 1
3 1
4 2
Another table holds user information
User
Id Name City
1 Peter LA
2 Harry SF
3 John NY
4 Joe CI
How do I make a statement which will give me All the users which are in company 1? Will something like
Select * from User where Id in (Select UserId from User_company where CompanyId = 1)
work?
SELECT * from User
left join User_company on User_company.UserId=User.Id
This would work...
SELECT * works but can be sluggish over time as it may not scale well with more data.
FROM User
WHERE Id in (Select UserId from User_company where CompanyId = 1)
So would this.. - best if you need data from both tables.
SELECT *
FROM User U
INNER JOIN User_Company UC
ON U.ID = UC.UserID
WHERE UC.CompanyID = 1
As would this - Probably the fastest if you just need data from user table.
Select * from User U
where exists (Select * from User_Company UC where U.ID = UC.UserID and CompanyID = 1)
OUTER joins are only needed if you need all records from one table and only those that match in another.
As to which is the best above: it depends on existing indexes and other requirements. Any of the above will return what's been asked for.
Try this
Select u.*
from User u
inner join User_company uc
on u.Id = uc.UserId
and uc.CompanyId = 1
BTW, what's wrong with the query you have posted? It will work as well fine. Just that it's a subquery and you better replace it with Join for performance.
Select * from User where Id in
(Select UserId from User_company where CompanyId = 1)
SELECT U.* FROM User AS U LEFT JOIN
User_company AS UC ON U.Id = UC.UserId WHERE UC.CompanyId = 1

how to write conditional left join

I have 2 tables named user and userFriend. I want all user from user table, and specific member from userFriend table. Then I want to join both of them...
user
userID userName
1 aaa
2 bbb
3 ccc
4 ddd
5 eee
userFriend
userFriendID userID friendUserID
1 1 2
2 2 3
3 1 4
4 4 2
So if my userID = 1,
then I want result like
userID userName userFriendID friendUserID
2 bbb 1 2
3 ccc NULL NULL
4 ddd 3 4
5 eee NULL NULL
so in this way I want conditional for 2nd table, I only want to join 2nd table having userID = 1 with 1st table using left join.
It's an unconventional thing to ask for... but this gives you the results you want.
SELECT u.userID, u.userName, uf.userFriendID, uf.friendUserID
FROM user u
LEFT JOIN userFriend uf ON u.userID = uf.friendUserID AND uf.userID =1
WHERE u.userID !=1
For what you want you dont need a query from both tables. Just using userFriend will give you the data. You'll need the user table for names only.
SELECT DISTINCT userID FROM userFriend
WHERE friendUserID = ?
this will give you all users that have that specific friend that you need.
Optionally you can add a INNER JOIN to see what are the names:
SELECT DISTINCT f.userID, u.name
FROM userFriend f
INNER JOIN users u ON u.userID = f.userID
WHERE friendUserID = ?
UPDATE
A comment about your structure. You don't need the userFriendID. The combination of userID and friend ID can be your key.
UPDATE of the query
SELECT * FROM users u
LEFT JOIN userFriend f on u.userID = f.userID
WHERE f.friendUserID = ?
This will return you all users that have the friend X. The left join is not important in this query. This specific condition is making it irrelevant.
To get exactly what you want you need to execute this.
SELECT * FROM users u
LEFT JOIN (
SELECT DISTINCT * FROM userFriend f
WHERE friendID = 4
) x ON u.id = x.userId
It can be done using a subquery, but I dont think is the most logical way of organizing your tables.