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.
Related
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).
I have a query as below:
users = select * from users where group_id = 1 and track_id = "xxxxxx";
user_ids = [user.id for user in users]
tickets = select * from tickets where group_id = 1 and user_id in (user_ids);
users and tickets tables all have the group_id field.
The index of users table are on group_id and track_id fields;
The index of tickets table are on group_id and user_id fields.
I want to merge two sql to one.
select * from tickets join users on tickets.user_id = users.id
where tickets.group_id = 1 and users.track_id = "xxxxx";
or
select * from tickets join users on tickets.user_id = users.id
where users.group_id = 1 and users.track_id = "xxxxx";;
Is it right? which is better? I how what is the meaning of inner join, but I want to know I should use tickets.group_id or users.group_id? What is the difference?
How to understand mysql inner join? How does it works?
You need to test both group_id fields, just like you do in the queries that you're merging.
SELECT *
FROM tickets AS t
JOIN users AS u ON t.user_id = u.user_id
WHERE t.group_id = 1
AND u.group_id = 1
Consider additional join criteria: If the group_ID will always be the same in both tables then you should add group_ID as join criteria and filter by either or.
SELECT *
FROM tickets join users
on tickets.user_id = users.id
and tickets.group_ID = users.group_ID
WHERE tickets.group_id = 1 and users.track_id = "xxxxx";
Alternatively you must filter based on both: because group_ID could be different than the ticket_ID, you must filter by both.
This most accurately represents the union of your two queries. if the group_ID is not the same for tickets and users when joined.
SELECT *
FROM tickets join users
on tickets.user_id = users.id
WHERE tickets.group_id = 1
and users.group_id = 1
and users.track_id = "xxxxx";
Logically however, we would need to understand the purpose of "GROUP_ID" in both tables. What is the functional purpose of each? I can envision selling off groups of tickets... but I don't see how a user could be in a static group as depending on the event, a user could be in more than one group over time.
Perhaps you shouldn't have group_ID in both tables? Perhaps you needa user Groups table...
If the two groups represent different fundamental attributes of the entity, then they must be evaluated separately; thus the 2nd query makes the most sense. However if they represent the same entity attribute then adding to the join criteria makes more sense
Your first query will give you all tickets for group 1 and the users could be in any group. The second query will give you all tickets for users in group 1 even if the ticket is in group 2.
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.
I wrote this SQL query to get friends of user who is not in the invitation table , and it works will , however i read that subqueries are performance eaters and I am not very good with JOINS, any help for modifying this query to be just in joins will be really appreciated.
Here is the SQL Query
SELECT friend.id,
friend.first_name
FROM friends AS friend
INNER JOIN friends_users AS friendsUser
ON ( friend.id = friendsUser.friend_id
AND friend.id NOT IN (SELECT friend_id
FROM friends_invitations
WHERE friends_invitations.user_id = 1) )
ORDER BY friendsUser.id ASC
Here is the tables structure
friends
id first_name
friends_users
id friend_id user_id
friends_invitations
id friend_id user_id
Any help will be really appreciated
A subquery is not necessarily a performance eater, is your query presenting performance issues?. Anyway, most times the fastest way to do your query would be using a NOT EXISTS:
SELECT friend.id,
friend.first_name
FROM friends AS friend
INNER JOIN friends_users AS friendsUser
ON friend.id = friendsUser.friend_id
WHERE NOT EXISTS (SELECT 1 FROM friends_invitations
WHERE friends_invitations.user_id = 1
AND friend_id = friend.id)
ORDER BY friendsUser.id ASC
Here is a link explaining the differences between NOT IN, LEFT JOIN and NOT EXISTS (for SQL Server). So, test this different options and choose the right one for your tables.
you can use LEFT JOIN for this,
SELECT friend.id,
friend.first_name
FROM friends AS friend
INNER JOIN friends_users AS friendsUser
ON friend.id = friendsUser.friend_id
LEFT JOIN friends_invitations
ON friend.id = friends_invitations.friend_id AND
friends_invitations.user_id = 1
WHERE friends_invitations.friend_id IS NULL
ORDER BY friendsUser.id ASC
I have three tables (user, friends, posts) and two users (user1 and user2).
When user1 adds user2 as friend then user1 can see the posts of user2 just like on Facebook. But only the posts after the date when user1 added user2 as friend. My query is like this:
$sql = mysql_query("SELECT * FROM posts p JOIN friends f ON
p.currentuserid = f.friendid AND p.time >= f.friend_since OR
p.currentuserid='user1id' WHERE f.myid='user1id'
ORDER BY p.postid DESC LIMIT 20");
it is working all the way fine but with a little problem.....!!
it displays user2, user3 (all the users as friends of user1) posts for single time but shows user1 posts multiple.......i.e
user2. hi
user1. userssfsfsfsfsdf
user1. userssfsfsfsfsdf
user3. dddddddd
user1. sdfsdsdfsdsfsf
user1. sdfsdsdfsdsfsf
but i in database it is single entry/post why it is happening........!!
How can I fix it?
I'm not a SQL expert, but I think your problem is in the JOIN condition. I cannot see a way how you can join with posts and friends and get the result that you need. A SQL expert may know this, but for me it's just too difficult.
If I were you I would break the problem down in 2 parts:
Select the user's own posts
Select the user's friend's posts
For example, you can do this by using 2 different conditions and do the join with the friends table in a sub query (I have not tested this!):
select *
from posts p
where
p.currentuserid = 'user1id'
or
p.postid in
(
select p2.postid
from posts p2
join friend f on p2.currentuserid = f.friendid
where p2.time >= f.friend_since and f.myid='user1id'
)
Another way to do it is to use a union (also not tested..):
select *
from posts p
where
p.currentuserid = 'user1id'
union
select p2.*
from posts p2
join friend f on p2.currentuserid = f.friendid
where p2.time >= f.friend_since and f.myid='user1id'
I think, the easiest solution is to use GROUP BY statement on column posts.userId to remove duplicate entries. However it is not optimized way to solve the problem.
The reason you're getting the posts of all of user1's friends is you're not qualifying which friend's posts the query should return.
Add a f.friendid = 'user2id' (or whatever the column name is) in there before the WHERE clause.
You really should give some idea of what the schema looks like so we don't have to make so many assumptions. I'm assuming the primary key of user is id, and friends has a userid as well as a friendid field. I'm also assuming posts.currentuserid is the id of the user who created the post. If not, replace it with posts.userid or whatever the correct field is.
The reason your query doesn't work right is that you need at least 2 joins. When creating a query, it's easiest to start with what you have and work up to what you want, one join at a time. Here's the query to get the posts that a particular user can read:
SELECT p.*
FROM user u
JOIN friends f ON u.id = f.userid
JOIN posts p ON ((u.id = p.currentuserid) OR (f.friendid = p.currentuserid AND p.time >= f.friend_since))
WHERE u.id = ?
ORDER BY p.postid DESC LIMIT 20
The second join is where the meat is. It specifies that in order to read a post it (a) has to be written by you or (b) has to be written by a friend of yours AFTER you friended them.
If you want to also get the name of the user who created the post (assuming user.name holds the user name) you need a 3rd join:
SELECT pu.name as 'Posted By', p.*
FROM user u
JOIN friends f ON u.id = f.userid
JOIN posts p ON ((u.id = p.currentuserid) OR (f.friendid = p.currentuserid AND p.time >= f.friend_since))
JOIN user pu ON p.currentuserid = pu.id
WHERE u.id = ?
ORDER BY p.postid DESC LIMIT 20