How to understand mysql inner join? - mysql

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.

Related

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.

MySQL JOIN ON the one of two columns that doesn't match variable

Let's jump right into it: I've got two simple tables set up in my MySQL database, a users table and a matches table. The users table holds, well, users. The matches table is meant to establish many-to-many connections between users and contains just two userID's.
What is want to query is a list of names of all matched users for the user with userID 1 but I can't wrap my head around it. The problem is that the userID (in this case 1) could be in either one field and I don't have a clue in which one.
Just to clarify; I mean something like this (please don't mind the weird pseudo-code):
SELECT users.name
FROM matches
INNER JOIN users
ON userId = (userId1 OR userId2 DEPENDS ON WHERE)
WHERE userId1 = '1'
OR userId2 = '1';
Could you please tell me if this is possible with MySQL and if so, what I should look for/if you would be so kind, give a simple example.
Thanks a lot.
The user of or in a join condition often prevents MySQL from using an index. The use of union or union all makes the query rather cumbersome. You can do what you want with left outer join:
SELECT coalesce(u1.name, u2.name) as name
FROM matches m LEFT JOIN
users u1
ON u.userId = m.userId1 AND m.userId2 = '1' LEFT JOIN
users u2
ON u.userId = m.userId2 AND m.userId1 = '1'
WHERE '1' in (m.userId1, m.userId2);
This should take advantage of indexes on users for looking up the values. If you want distinct names, then add the distinct keyword.
Try:
SELECT DISTINCT u.name
FROM matches m
INNER JOIN users u
ON (u.userId = m.userId1 AND m.userId2 = '1')
OR (u.userId = m.userId2 AND m.userId1 = '1')
Added DISTINCT to avoid duplicate rows.
See this fiddle.
Here's one way to do it that avoids excessive JOIN logic (to make sure SQL can use indexes on users.userId, matches.userId1, matches.userId2)
SELECT u.`name`
FROM
matches AS m
JOIN users AS u
ON m.userId1=u.userId
AND m.userId2='1'
UNION
SELECT u.`name`
FROM
matches AS m
JOIN users AS u
ON m.userId2=u.userId
AND m.userId1='1'
Something like this:
Select UserId1, UserId2
From Matches
Where UserId1 = 1
Union
Select UserId2, UserId1
From Matches
Where UserId2 = 1
Notice the order of the UserIds have been changed in the Select clause. This will give you a single list of matches with you searched user '1' in a single column and all their matches in the other.
This approach will require you then link in your users table as follows:
Select searchmatches.UserId1, searchmatches.UserId2, leftuser.Name, rightuser.name
From (
Select UserId1, UserId2
From Matches
Where UserId1 = 1
Union
Select UserId2, UserId1
From Matches
Where UserId2 = 1
) searchmatches
inner join users leftuser userMatches.UserId1 = leftuser.UserId
inner join users rightuser userMatches.UserId2 = rightuser.UserId
Hope that Helps! If you want you can remove one of the inner joins to the users table as you know who the left user is as you searched on them!

Selecting using two column names, using the other one if one is known of each record

I'm not entirely sure how to phrase this as a concise question title.
I have a games table which contains of records representing games of a player against another. The relevant columns of this table are userid1 and userid2. I have another table called accounts, consisting of the columns id and username.
I'd like to fetch the username of the opponent from the accounts table. The opponent is the user of which the id is not equal to the known user id. So, if userid1 = 123 then I'd like to fetch accounts.username of the user where accounts.id is equal to userid2 of the same games record. The same goes the other way round if userid2 = 123.
I've so far only been able to select the opponent's username separately using two queries:
SELECT * FROM games, accounts
WHERE games.userid1 = 123 AND accounts.id = games.userid2
and:
SELECT * FROM games, accounts
WHERE games.userid2 = 123 AND accounts.id = games.userid1
(swapped: ^ ^)
However, I'd like to combine these two queries nicely into one. So, is it possible to get accounts.username where accounts.id is equal to:
games.userid2 if games.userid1 is equal to the known user id
games.userid1 if games.userid2 is equal to the known user id
and if so, how?
You can use a case statement in your join condition, something like this:
SELECT * FROM games g
JOIN accounts a
ON a.id = case g.userid1 when ? then g.userid2 else g.userid1 end
WHERE
g.userid1 = ? OR g.userid2 = ?
However, depending on your indexes, it may be quicker to use a union, eg.
SELECT * FROM games g
JOIN accounts a ON a.id = case g.userid2
WHERE g.userid1 = ?
UNION ALL
SELECT * FROM games g
JOIN accounts a ON a.id = case g.userid1
WHERE g.userid2 = ?
An alternative query using OR,
SELECT * FROM games g, accounts a
WHERE
(g.userid1 = ? AND g.userid2 = a.id)
OR (g.userid2 = ? AND g.userid1 = a.id)

Match results in multiple tables MYSQL

Hi I have a query that is giving me a few problems and it was suggested I ask a separate question about the end result rather than the problem.
So I have three tables and some user input.
the tables are:
users,
usersLanguages and
usersSkills
each table has a related ID the users table has ID and on the other two they have userID to match skills and languages to users
the user input is dynamic but for example it can be 1 for usersLanguages and 2 for usersSkills
The user input is taken from a form and what i need to do is match get the results of users
depending on the language IDs or skill ids passed through. for example i can pass two user ids and three language ID's.
SELECT DISTINCT users.ID, users.name
FROM users
INNER JOIN usersSkills
ON users.ID = usersSkills.userID
INNER JOIN usersLanguages ON users.ID = usersLanguages.userID
WHERE activated = "1"
AND type = "GRADUATE" AND usersSkills.skillID IN(2)
AND usersLanguages.languageID IN(2)
GROUP BY usersSkills.userID HAVING COUNT(*) = 1,
usersLanguages.userID HAVING COUNT(*) = 1
You do not mix group by and having clauses.
having is not a part of group by, in fact you can have having without a group by, in which case it will work as a more capable (and slower) where clause.
SELECT u.ID, u.name
FROM users u
INNER JOIN usersSkills us ON u.ID = us.userID
INNER JOIN al ON u.ID = ul.userID
WHERE u.activated = '1'
AND u.type LIKE 'GRADUATE' AND us.skillID IN('2')
AND ul.languageID IN('2')
GROUP BY u.ID
HAVING COUNT(*) = 1
because of the join criteria, ul.userID = us.userID = u.ID so it makes no sense to group by both. Just group by u.id, because that's the ID you select after all.
u.name is functionally dependent on ID, so that does not need to be listed (*).
When doing a test like u.type = 'GRADUATE', I prefer to do u.type LIKE 'GRADUATE' because LIKE is case insensitive and = may not be depending on the collation, but that's just me.
Distinct is not needed; group by already makes the results distinct.
Having works on the resultset up to and including group by, so in this case you need only one having clause. If you want to have more, you need to treat them the same as a where clause:
having count(*) = 1 AND SUM(money) > 10000
(*) this is only true in MySQL, in other SQL's you need to list all non-aggregated selected columns in your group by clause. Whether they are functionally dependent on the group by column or not. This slows things down and makes for very long query-syntax, but it does prevent a few beginners errors.
Personally I like the MySQL way of doing things.
something like
SELECT * from users left join usersLanguage on users.id=usersLanguage.userID
left join usersSkills on usersSkills.userID=users.id
where usersLanguage.id in (1, 2, 3) and usersSkills.id in (1, 2, 3)
GROUP BY users.id
will probably work
try this
SELECT users.ID,user.name
FROM users
INNER JOIN usersSkills ON users.ID = usersSkills.userId
INNER JOIN AND usersLanguages ON users.ID = usersLanguages.userID
WHERE activate = '1'
AND type = 'GRADUATE'
AND usersSkill.skillID IN (2)
AND usersLanguages.languageID IN (2)
GROUP BY users.ID