Ok that title hardly describes the issue. But Im stuck on how to solve this, so Ill just try to describe it.
I have two mySql table: listings, userVotes
Listings has fields (id, description, votes)
userVotes has fields (userId, listingsId)
Well Im trying to get the join of these tables so the result is (id, description, votes, userId, listingsId, vote)
So statement would normally be "select * join on id = listings id" (paraphrased)
But this led to problems because I still need the listing to show even if there was no userVote for it.
So I changed it to "select * left join" and that allowed me to retrieve the listing even if there was no userVote associated with it.
But this led to another problem. I need to have the constraint "where userId = 'foo'"; on the result. But this doesnt work because again it leaves out the listings with no userId.
So essentially I need the statement:
"Select * from listings l left join userVote u on l.id = u.listingId where if exists userId = 'foo'"
Is it doable?
You can just move the condition to the ON clause of the left join to not make it restrict the resulting rows;
SELECT *
FROM listings l
LEFT JOIN userVote u
ON l.id = u.listingId
AND u.userId = 'foo'
Any restriction you put in the WHERE clause will remove results, while any you put in the ON clause of a left join will only set the userVote to NULL if not matched.
Related
I have already read similar questions, but it does not help me.
I have query
SELECT `login`,
`photo`,
`username`,
`user`.`id`,
`name`,
`msg_info`
FROM `user`
LEFT JOIN `friends`
ON `friends`.`child` = `user`.`fb_id`
WHERE `friends`.`parent` = '1111'
ORDER BY `msg_info` DESC
Which tooks 0.7411 seconds (and even more)
It shows 158 total rows (ok i can limit it, but query still slow)
Each of tables friends and user has more than 200.000 rows
What can i do for query go faster?
Thank you!
As the comments pointed out, your left join is really not different than the following inner join query:
SELECT
login,
photo,
username,
user.id,
name,
msg_info
FROM user u
INNER JOIN friends f
ON f.child = u.fb_id
WHERE
f.parent = '1111'
ORDER BY
msg_info DESC;
We can try adding an index to the friends table on (parent, child, name, msg_info, ...). I am not sure which other columns belong to friends, but the basic idea is to create an index on parent, to speed up the WHERE clause, and hopefully take advantage of low cardinality on the parent column. Then, we include the child column to speed up the join. We also include all the other columns in the select clause to let the index cover the other columns we need.
CREATE INDEX idx ON friends (parent, child, name, msg_info, ...);
As #MrVimes suggeted, sometimes adding a condition to the JOIN clause can make a big difference:
SELECT login, photo, username, user.id, name, msg_info
FROM user u
INNER JOIN friends f ON f.child = u.fb_id AND f.parent = '1111'
ORDER BY msg_info DESC;
Assuming, of course, all your PK and FKs are properly defined and indexed.
I have a products table where I include 3 columns, created_user_id, updated_user_id and in_charge_user_id, all of which are related to my user table, where I store the id and name of the users.
I want to build an efficient query to obtain the names of the corresponding user_id's.
The query that I build so far is the following:
SELECT products.*,
(SELECT name FROM user WHERE user_id = products.created_user_id) as created_user,
(SELECT name FROM user WHERE user_id = products.updated_user_id) as updated_user,
(SELECT name FROM user WHERE user_id = products.in_charge_user_id) as in_charge_user
FROM products
The problem with this query is that if I have 30,000 records, I am executing 3 more queries per row.
What would be a more efficient way of achieving this? I am using mysql.
For each type of user id (created, updated, in_charge) you would JOIN the users table once:
SELECT
products.*,
u1.username AS created_username,
u2.username AS updated_username,,
u3.username AS in_charge_username,
FROM products
JOIN user u1 ON products.created_user_id = u1.user_id
JOIN user u2 ON products.updated_user_id = u2.user_id
LEFT JOIN user u3 ON products.in_charge_user_id = u3.user_id
This is the best practice method to obtain the data.
It is similiar to your query with sub-selects but a more modern approach which I think the database can optimize and utilize better.
Important:
You need foreign key index on all the user_id fields in both tables!
Then the query will be very fast no matter how many rows are in the table. This requires an engine which supports foreign keys, like InnoDB.
LEFT JOIN or INNER JOIN ?
As the other answers suggest a LEFT JOIN, I would not do a left join.
If you have an user id in the products table, there MUST be a linked user_id in the user table, except for the in_charge_user which is only present some times. If not, the data would be semantically corrupt. The foreign keys assure that you always have a linked user_id and a user_id can only be deleted when there is no linked product left.
JOIN is equivalent to INNER JOIN.
You can use LEFT JOIN instead of subselects.
Your query should be like:
SELECT
P.*,
[CU].[name],
[UU].[name],
[CU].[name]
FROM products AS [P]
LEFT JOIN user AS [CU] ON [CU].[user_id] = [P].[created_user_id]
LEFT JOIN user AS [UU] ON [UU].[user_id] = [P].[updated_user_id]
LEFT JOIN user AS [CU] ON [CU].[user_id] = [P].[in_charge_user_id]
First, your query should be fine. You only need an index on user(user_id) or better yet user(user_id, name) for performance. I imagine that the first exists.
Second, you can write this using LEFT JOIN:
SELECT p.*, uc.name as created_user,
uu.name as updated_user, uin.name as in_charge_user
FROM products p LEFT JOIN
user uc
ON uc.user_id = p.created_user_id LEFT JOIN
user uu
ON uu.user_id = p.updated_user_id LEFT JOIN
user uin
ON uin.user_id = p.in_charge_user_id;
With one of the above indexes, the two methods should have very similar performance.
Also note the use of LEFT JOIN. This handles the case where one or more of the user ids is missing.
Try this below query
SELECT products.*, c.name as created_user,u.name as updated_user,i.name as in_charge_user
FROM products left join user c on(products.created_user_id=c.user_id ) left join user u on(products.updated_user_id=u.user_id ) left join user u on(products.in_charge_user_id=i.user_id )
Also as Gordon Linoff mentioned create index on user table will fetch your data faster.
In this sql:
SELECT s.*,
u.id,
u.name
FROM shops s
LEFT JOIN users u ON u.id = s.user_id
OR u.id = s.owner_user_id
WHERE s.status = 1
For some reason this query takes an amazing time. although id is the primary key. it seems especially after I added this part OR u.id=s.owner_user_id the query became slow. owner_user_id often is 0 only handful of times. But why would it take so long apparently scanning the whole table? The database table users is very long and big. I didn't design it. this is for a client who subsequent programmers added too many fields. the table is 22k rows and dozens of fields.
*the names of the fields for demonstration only. actual names are different, so don't ask me why I'm looking for owner_user_id (; I did solve the slowness by remove the "OR ..." part and instead searching for the id in the loop if it is not 0. but I would like to know why this is happening and how to speedup that query as is.
You may be able to speed it up by using IN instead of the OR but that is minor.
SELECT u.id,
u.name
FROM shops s
LEFT JOIN users u ON u.id IN ( s.user_id, s.owner_user_id )
WHERE s.status = 1
Firstly, are there any indexes on this table? Mainly one on the user.id field or the s.user_id or s.owner_user_id?
However, I must ask why you need to use a LEFT JOIN instead of a regular join. The LEFT JOIN causes the matching of every row with every other one. And since I'm assuming the value / id should either be in the user_id or the owner_user_id field, and that there will always be a match, if that is the case then the use of a JOIN should speed the query up a bit.
And as Mitch said, 22k rows is tiny.
How are you going to know which user record is which? Here's how I'd do it
SELECT s.*,
u.name AS user_name,
o.name AS owner_name
FROM shops s
LEFT JOIN users u ON s.user_id = u.id
LEFT JOIN users o ON s.owner_user_id = o.id
WHERE s.status = 1
I've omitted the IDs from the user table in the SELECT as these will be part of s.* anyway.
I'm curious about the left joins too. If shops.user_id and shops.owner_user_id are required foreign keys, use inner joins instead.
I have a db structure like:
posts
id
title
content
users
id
....
post_reads
post_id
user_id
How can I count the number of posts for which a particular user with an id say, x does not have a read record.
My SQL query currently looks like:
SELECT COUNT(posts.id) AS c
FROM `posts`
LEFT JOIN `post_reads` ON (`posts`.`id` = `post_reads`.`post_id`)
LEFT JOIN `users` ON (post_reads.user_id = `users`.`id` AND post_reads.user_id = x)
WHERE users.id IS NULL
AND post_reads.user_id IS NULL
I know I'm doing something wrong, although I'm not sure what that is.
This should to the trick
SELECT COUNT(posts.id) AS c
FROM posts
LEFT JOIN post_reads ON posts.id = post_reads.post_id AND post_reads.user_id = x
LEFT JOIN users ON post_reads.user_id = users.id
WHERE users.id IS NULL
Note that if you're not interested in doing anything with table users you can shorten this query to:
SELECT COUNT(posts.id) AS c
FROM posts
LEFT JOIN post_reads ON posts.id = post_reads.post_id AND post_reads.user_id = x
WHERE post_reads.user_id IS NULL
The first join you were doing is really an inner join, because it will never 'misfire'.
The second join will sometimes misfire, because you have the extra condition in there.
Therefore using the post_reads.some_id is null will never be true.
In order for that to work you'd have to repeat the AND post_reads.user_id = x in that join condition as well, but putting it in twice is silly and not needed, once will do.
PS don't forget to replace the 'x' with something more useful :-)
I tried this a few ways just using JOINS/WHERE, but they tend to miss certain cases (i.e. you can exclude posts joined to a read record for the given user, but the posts' ids will still be returned if they also join to read records for other users).
The simplest way may be something like this:
SELECT COUNT(DISTINCT id)
FROM posts
WHERE id NOT IN (SELECT DISTINCT post_id FROM post_reads WHERE user_id = #x)
Also, note that I don't believe you need to surround identifiers in backticks unless they are MySQL keywords.
I've spent a bit of time researching this on here and the mysql site but I'm a bit confused on two things: which sort of join to use and how (or if) to use an alias.
The query:
SELECT forum_threads.id, forum_threads.forum_id, forum_threads.sticky,
forum_threads.vis_rank, forum_threads.locked, forum_threads.lock_rank,
forum_threads.author_id, forum_threads.thread_title, forum_threads.post_time,
forum_threads.views, forum_threads.replies, users.username AS author_username
FROM forum_threads LEFT JOIN users ON forum_threads.author_id = users.id
WHERE forum_threads.forum_id=XXX
Now that query currently finds all threads from the given forum and joins the threads author id to the username table. I also have lastpostid which I'd also like to include in that query and join again on the users table so I can get the username for the last poster too.
I tried adding:
LEFT JOIN users ON threads.lastpostid = users.username
but that just results in an alias error as users isn't unique.
I also tried using both an alias on the main query and on the second join but it keeps giving me missing field errors.
Could someone give me a point in right direction please?
Yes, you need a different alias each time. Every time you refer to the table in the query you should use the approprate alias.
SELECT
forum_threads.id,
-- etc...,
forum_threads.replies,
u1.username AS author_username
u2.username AS last_post_username
FROM forum_threads
LEFT JOIN users u1 ON forum_threads.author_id = u1.id
LEFT JOIN users u2 ON threads.lastpostid = u2.username
WHERE forum_threads.forum_id=XXX