MySql Query: Logical construction - mysql

I am quite new in MySql and I am struggling with a query.
I have 3 tables:
-users
-user_id
-username
-password
-groups
-group_id
-group_name
-user_group_link
-id
-user_id (foreign key of users.user_id)
-group_id (foreign key of groups.group_id)
Basically, I want the user_id of one user which has not joined the group with the id 5 (Every user can join multiple groups but can only join it once. Every group can have multiple users).
So something like this...
SELECT users.user_id
FROM users, user_group_link
WHERE users.user_id NOT IN (
UNION SELECT user_group_link.user_id, user_group_link.group_id
WHERE user_group_link.group_id = 5)
Hopefully this can explain what I am trying to select.
I would really appreciate if someone could help me construct this query.
Im stuck here for about 2 days and I really dont know what the Google for. I noticed that UNION SELECT or an INNER JOIN would fit for me.

Basically, the approach is to find all user_id available in user_group_link and then check which user_id from users table is not available in list of id from user_group_link.
The one option could be:
SELECT users.user_id
FROM users
WHERE users.user_id NOT IN (SELECT user_group_link.user_id FROM user_group_link
WHERE user_group_link.group_id = 5)

I want the user_id of one user which has not joined the group with the id 5
"I want the ids of all users that are not linked to group 5":
select user_id
from users u
where not exists (select 1
from user_group_link
where user_id = u.id
and group_id = 5)

Perfect opportunity to use EXCEPT.
SELECT user_id
FROM users
EXCEPT
SELECT user_id
FROM user_group_link
WHERE group_id = 5
This query will first select all user_ids from user_group_link table that ARE in group 5 and then it will select all user_ids from users table except these ids (the ids of the users in group 5).

Related

MySQL - how to combine three tables to get the counts

There are three tables, I would like to get the count of a user's total tweets and the count of likes his total tweets received.
I tried to combine two queries to get what I want but failed. Have looked through several previous questions but still can't figure it out.
Users table
id
name
1
User1
Tweets table
id
UserId (foreign key)
content
1
User1
hello
Likes table
id
UserId (foreign key)
TweetId (foreign key)
1
User1
hello
First query:
SELECT Users.name, Users.id, COUNT(Tweets.UserId) AS UserTweetCount FROM Users
LEFT JOIN Tweets
ON Users.id = Tweets.UserId
GROUP BY Users.id
ORDER BY UserTweetCount DESC;
Second query:
SELECT Users.name, Users.id, COUNT(Likes.UserId) AS UserTweetBeLikedCount FROM Users
LEFT JOIN Likes
ON Users.id = Likes.UserId
GROUP BY Users.id;
I tried like below but would get wrong UserTweetBeLikedCount counts. The counts would be UserTweetCount's, not UserTweetBeLikedCount's. When I ran two queries separately, it worked well. But when I combined them together, it didn't work right.
Don't know how to display the right counts. Can someone give me hints to solve this, please?
SELECT Users.name, Users.id,
COUNT(Tweets.UserId) AS UserTweetCount, COUNT(Likes.UserId) AS UserTweetBeLikedCount
FROM Users
LEFT JOIN Tweets
ON Users.id = Tweets.UserId
LEFT JOIN Likes
ON Users.id = Likes.UserId
GROUP BY Users.id
ORDER BY UserTweetCount DESC;
I recommend using correlated subqueries for this:
SELECT u.*,
(SELECT COUNT(*)
FROM Tweets t
WHERE u.id = t.UserId
) AS UserTweetCount,
(SELECT COUNT(*)
FROM Likes l
WHERE u.id = l.UserId
) AS UserLikeCount
FROM Users u
ORDER BY UserTweetCount DESC;
As a note: For performance, you want indexes on Tweets(UserId) and Likes(UserId).

MySQL - How to select records that match all IN values but in 1 or more tables

Good day, I can't seem to figure out how to do this. I'll first explain my database model:
User (user_id, name)
Job (job_id, name)
UserTopJob (user_id, job_id)
UserOtherJob(user_id, job_id)
A user can setup his top jobs which he likes best. Those values will be saved into UserTopJob by the user_id and the job_id. The user can set some other jobs he likes into UserOtherJob as well.
Now, what I want to do is query out users that match my job search input.
For example, the search input is job_id 1 and 2.
Now I want to query out the users that match BOTH job_id 1 and job_id 2, but it doesn't matter whether they are in the users top or other jobs, or divided between those two tables.
So a user must be returned if:
Both job_id 1 & 2 are in top jobs
Both job_id 1 & 2 are in the other jobs
They have both job_id 1 and 2 but in different tables
The number of input ids can grow and does not have a limit. It must always match ALL input values.
Edit: So, for example if I'm putting job_ids 1 and 2 and 3 into the query, the ids 1 AND 2 AND 3 need to be in the top or other table for that user.
Can anybody please help me create a MySQL-query that can do this and doesn't put too much pressure on db-performance?
Thanks in advance for helping me out here!
You can use UNION for this type of work.
SELECT user_id AS user FROM UserTopJob where job_id in {job_ids}
UNION
SELECT user_id AS user FROM UserOtherJob where job_id in {job_ids};
Try this query:
SELECT u.*
FROM User u
WHERE NOT EXISTS (
SELECT 1
FROM User u0
JOIN Job j ON j.job_id IN (1,2) -- or other list of job ids
LEFT JOIN UserTopJob utj ON utj.user_id = u0.user_id AND utj.job_id = j.job_id
LEFT JOIN UserOtherJob uoj ON uoj.user_id = u0.user_id AND uoj.job_id = j.job_id
WHERE u0.user_id = u.user_id
AND utj.job_id IS NULL
AND uoj.job_id IS NULL
)
Test in on SQL Fiddle
You can do a JOIN between the tables to get the required result like
select u.name as user_name,
j.name as job_name
from `user` u
INNER join usertopjob utj on u.user_id = utj.user_id
inner join userotherjob uoj on u.user_id = uoj.user_id
inner join job j on j.job_id = utj.job_id or j.job_id = uoj.job_id
where j.job_id in (1,2);
Alright, this was a brain buster this evening. Toying around with this for some time I came up with this and it seems to work.
SELECT user_id, SUM(matched) AS totalMatched FROM
(
SELECT uoj.user_id, COUNT(uoj.job_id) AS matched FROM userOtherJob AS uoj
INNER JOIN user AS u ON u.user_id = uoj.user_id
WHERE uoj.job_id IN (1,2)
GROUP BY u.user_id
UNION ALL
SELECT utj.user_id, COUNT(utj.job_id) AS matched FROM userTopJob AS utj
INNER JOIN user AS u ON u.user_id = utj.user_id
WHERE utj.job_id IN (1,2)
GROUP BY u.user_id
) AS t
GROUP BY user_id
HAVING totalMatched = 2
This query counts the matches in the 'other' table, after that the matches in the 'top' table, and sums the totals of both tables. So, the total number of matches (combined from top and other) must be the same value as the number of jobs we're looking for.

SQL query, which one is more sufficient?

I have a database with three tables:
post
user_id
friendship
user_id
friend_id
status
I want to select all posts whose author (user_id) is current_user's friend
I am wondering which one of the following statement is more efficient? (1 is current user's id, 2 is the code for being friend)
1)
SELECT * FROM posts
INNER JOIN friendships AS fs
ON fs.user_id=1 AND fs.friend_id=posts.user_id AND fs.status=2;
2) query twice
SELECT id FROM friendships
WHERE user_id = 1 AND status = 2
# Assuming get (1,2,3)
SELECT * FROM posts WHERE user_id IN (1,2,3)
Can anyone tell me which is faster? Because I do not have enough data, I can not test....
Neither. Join your tables in the same order you would access them as a human:
SELECT p.*
FROM friendships f
JOIN posts p ON p.user_id = f.friend_id
WHERE f.user_id = 1
AND f.status = 2
Here an index will be used based on the WHERE clause to find rows in friendships, which will then be used to access posts via an index.
Make sure there are indexes on posts.user_id and friendships.user_id.

How to check if id exists in another table given table is 30 million records?

I know the question seems duplicate, but I don't know how to ask it differently.
I have two very simple tables in MySQL database, The first is table Users
id, user_id
1 1
2 3
4 4
The second is table Friends
id, user_id, friend_id
1 1 3
2 1 4
3 1 8
I dumped the data from CSV file that I would like to clean. I need to check if friend_id exists in table 1 as well. The first table has around 30000 rows, but the second table has around 30 million rows.
And I use this query to check
SELECT u.user_id, uf.friend_id as exists_friend_ids
FROM Users u, Friends uf
WHERE u.user_id = '1'
and uf.friend_id IN (select user_id from eventify.Users)
However, my desired output would be this but as I cannot run the above query to actually give my test results I cannot continue.
user_id, exists_friend_ids
1 3
1 4
You can see that 8 is not there, because it doesn't exist in Users table. But as the second table has over 30 million records it's just running forever on my computer. Am I doing it right or this is the only way to do it. Or should I learn Hadoop instead?
I have updated my query to use equal join.
Have you tried a LEFT JOIN query with a GROUP BY friend_id ? If a user doesn't exist, it won't add a line to the result.
If all you are doing is cleaning the table then you have some flexibility since the fact that the query runs slow will not have a great impact since you will want to run it only once. Here are a couple of different options:
use a left join to find the rows in friends without the corresponding friend id in the users table (untested):
SELECT Friends.id, Users.user_id
FROM Friends LEFT JOIN Users on Friends.friend_id = Users.user_id
WHERE Users.user_id is NULL
Then delete the records you find
use an inner join to fin the friends that exist. Then create a new table with those records (untested)
SELECT Friends.id, Users.user_id
FROM Friends INNER JOIN Users on Friends.friend_id = Users.user_id
And insert the resulting rows into a new table which will become your new "Friends" table.
Hope that helps
I don't understand why you do the CASE construct here. If you want to get a list of all friend_ids that don't exist in the users table, then what about something like:
select friends.friend_id,
count(*)
from friends
where friends.friend_id not in (select users.user_id
from users)
group by 1
You will of course have an index on users.user_id...

query to join a table with two fields into one list containing items in both fields

Looking at similar questions, I actually want the exact opposite of this:
SQL query for getting data in two fields from one column
I have a table meetings with paired users:
A_user_id | B_user_id
1 2
3 4
There is a user table as well.
Is there a simple mysql query that lists all the user_ids into one long list?
query result
1
2
3
4
I was thinking something like this but it doesn't work:
select *
from user
where user.id in (
(select A_user_id from meeting)
or
(select B_user_id from meeting)
)
Thanks!
UPDATE (UNION solved this, but let's make this a bit more challenging):
I want to get a list of usernames and location names (both are reference tables) so I need to join this union query to them. Here's what I tried:
select u1.fname, l1.name
from meeting m1
join user u1 on m1.A_user_id=u1.id
join locations l1 on m1.location_id=l1.id
union
select u2.fname, l2.name
from meeting m2
join user u2 on m2.A_user_id=u2.id
join locations l2 on m2.location_id=l2.id
order by location_id asc
I'm getting two errors:
1- Not sure what kind of joins I need on these. (without the last 'order by' line) I'm getting a list of only 2 (there should be 4, as there are 2 pairs of people meeting). It seems to be pulling only the first item from each part of the union. I believe this relates to the type of join I'm doing for each, but not sure. So, users are distinct (there is only 1 user in the meeting table and it matches only 1 user in the user table), but locations are not (2 users are meeting at 1 location, and I think when I join on locations it is messing things up).
2- How do I use the "order by" at the end to order by the resulting list of "location_id"s, since now I have two named tables to deal with.
Thanks!
UPDATE 2:
Ok I put the two selects into parenthesis and UNIONed them and now I can order by the location_id... but I still have no idea how to join on the location table. Mysql doesn't like what I tried
(select u1.fname, m1.location_id
from meeting m1
join user u1 on m1.A_user_id=u1.id)
union
(select u2.fname, m2.location_id
from meeting m2
join user u2 on m2.B_user_id=u2.id)
#join locations l on l.id = location_id // this line messes things up *
order by location_id asc
Doesn't there need to be an all encompassing select around this whole thing?
How do I join the locations.id field on the "location_id" field that gets kicked off of the union query? Since the "location_id" field is technically in two different tables?
THe join above throws an error.
UPDATE 3: SOLVED
Here's my final query:
select tb1.fname, l.name
from (
(select u1.fname, m1.location_id
from meeting m1
join user u1 on m1.A_user_id=u1.id)
union
(select u2.fname, m2.location_id
from meeting m2
join user u2 on m2.B_user_id=u2.id)
) tb1
join locations l on l.id = tb1.location_id
order by location_id asc
select A_user_id as id from meetings
union
select B_user_id as id from meetings
in your example code, you could use an 'or', but the 'or' has to join two 'in' statements, if you get what I mean.
select *
from user
where
(
(user.id in (select A_user_id from meeting))
or
(user.id in ((select B_user_id from meeting))
)
And to answer you second update, you want something like
select locations.* from
(
(select A_user_id as id from meeting)
union
(select B_user_id as id from meeting)
) as UIDS
join
locations on locations.id = UIDS.id
select A_user_id as user_id from meetings
union all
select B_user_id as user_idfrom meetings
order by user_id
Notes:
UNION ALL keeps duplicates, UNION doesn't
Any ORDER BY goes at the end of the UNION