Optimizing sql double "Not in" clauses - mysql

I have the following query:
select * from table_2
where
user_1 not in (select user_id from users) or
user_2 not in (select user_id from users)
The problem is that it takes very long time to execute. The tables have few millions of records. Is there any optimization that I can use for the query?

First, rewrite these as not exists . . . this often has better performance:
select t2.*
from table_2 t2
where not exists (select 1 from users u where u.user_id = t2.user_1) or
not exists (select 1 from users u where u.user_id = t2.user_2);
More importantly, create an index on users(user_id), if one does not already exist.

You should try this:
select * from table_2
LEFT JOIN `users` AS u ON u.user_id = table_2.user_1
LEFT JOIN `users` AS u2 ON u2.user_id = table_2.user_2
WHERE u.user_id is NULL and u2.user_id is NULL

This query will return all rows where user_1 and/or user_2 are not in the users table
select * from table_2 t1
where (select count(*) from users u where user_id in(t1.user_1,t1.user_2)) < 2

SELECT * FROM table_2 t2
WHERE
NOT EXISTS( SELECT 1 FROM users u WHERE u.user_id IN(t2.user_1,t2.user_2) )
this should perform better.

Related

MySQL: Putting condition in SELECT query based on column value

I have two tables user_profiles and user_friends.
user_profiles has columns id, user_privacy and few other columns (like username, age etc).
user_friends has columns user_id and friends_id. One user_id can have multiple friend_ids
This query simply returns profile of user having id, say, 1997:
select * from user_profiles
where prfls.id=1997;
And this query returns profile of user having id 1997 only when it has got friend having id, say, 2001:
select * from user_profiles prfls
inner join user_friends frnds on (prfls.id=frnds.user_id)
where prfls.id=1997 and frnds.friends_id=2001;
However, I want to write a single query that will check if column user_privacy (in user_profiles) for user 1997 is false then the query shouldn't check for friends_id in user_friends. It should simply return profile of user 1997. But if the user_privacy is true then only it should check for it.
How can this query be written? (Preferably using joins and without using sub-queries)
Use left join for it:
select distinct t1.* from user_profiles t1
left join user_friends t2 on(t1.id = t2.user_id)
where t1.id = 1997 and
(user_privacy='false' or t2.friends_id = 2001)
Try this;)
select t1.*
from user_profiles t1
where t1.id = 1997
and (
t1.user_privacy = 'false'
or exists (select 1 from user_friends t2 where t1.id = t2.user_id and t2.friends_id = 2001)
)
Without subquery, you can try this;)
select distinct t1.*
from user_profiles t1
inner join user_friends t2
on t1.id = 1997 and (t1.user_privacy = 'false' or (t1.id = t2.user_id and t2.friends_id = 2001))
SELECT *
FROM user_profiles P
LEFT JOIN user_friends F ON F.user_id = P.id
AND P.user_privacy = true
AND F.friends_id = 2001
WHERE P.id = 1997
Use LEFT JOIN so even if user_privacy is false, the query will still return the user_profiles
Add condition in the ON clause of LEFT JOIN indicating that it will return the user_friends if user_privacy is true.
Move the filtering of F.friends_id in the ON clause of the LEFT JOIN. (Thanks to #Msf vtp for verifying, cheers!)

MySQL JOIN Multiple Tables With LIMIT Last Table By DateTime per result

I got an interesting one. I have 3 tables I am joining. The last table I joined, I want to only see the latest entry by date and not double up my results with the same user and just have different login times.
Example (this is a sample table only):
SELECT a.user_id
a.user_name,
b.department,
c.last_logon_date_time,
c.computer_name
FROM table1 a
LEFT OUTER JOIN table2 b ON a.user_id = b.user_id
LEFT OUTER JOIN table3 c ON c.user_id = c.user_id
Below will give me the results I am looking for but looks very inefficient and is really slow. Any way of speeding this up and making it more efficent?
I could do this
SELECT a.user_id
a.user_name,
b.department,
(SELECT c.last_logon_datetime FROM table 3 c WHERE a.user_id = c.user_id ORDER BY c.last_logon_datetime DESC LIMIT 1) as last_logon_datetime,
(SELECT c.computer_name FROM table 3 c WHERE a.user_id = c.user_id ORDER BY c.last_logon_datetime DESC LIMIT 1) as computer_name
FROM table1 a
LEFT OUTER JOIN table2 b ON a.user_id = b.user_id
Thank You.
You can JOIN to a "table" created from a subselect like this:
SELECT t1.user_id, t1.user_name, t2.department, t3.computer_name, t3.logon_date
FROM table1 AS t1
LEFT OUTER JOIN table2 AS t2 ON t1.user_id = t2.user_id
LEFT OUTER JOIN
(SELECT user_id, computer_name, MAX(last_logon_date) AS `logon_date` FROM table 3 GROUP BY computer_name) AS t3 ON t1.user_id = t3.user_id

complex sql statement to different rows

I have a simple table which has 4 fields:
indexID
UserID
text_1
IsFinal
This table may have multiple values for each UserID, and IsFinal field can have only a single value 1 or 0.
What I'm trying to do is to make a select statement which will return the user IDs if IsFinal only equal 0. The problem there may be multiple records for the same userID, some having IsFinal equal to 0 and only 1 with IsFinal equal to 1.
My problem here is this: for every UserID, if it has a record with Isfinal = 1, I want to ignore all records with the same UserID, otherwise I want to return its records. I don't know if that can be done by SQL statement only or not.
Seems like you want an anti-join, i.e. you first need to establish which user IDs have IsFinal = 1, then use that result set to return all user IDs not in that list.
There are various ways to implement an anti-join.
NOT IN:
SELECT *
FROM atable
WHERE UserID NOT IN (
SELECT UserID
FROM atable
WHERE IsFinal = 1
);
NOT EXISTS:
SELECT *
FROM atable t1
WHERE NOT EXISTS (
SELECT *
FROM atable t2
WHERE t1.UserID = t2.UserID
AND t2.IsFinal = 1
);
LEFT JOIN + WHERE IS NULL:
a)
SELECT *
FROM atable t1
LEFT JOIN (
SELECT *
FROM atable
WHERE IsFinal = 1
) t2 ON t1.UserID = t2.UserID
WHERE t2.UserID IS NULL;
b)
SELECT *
FROM atable t1
LEFT JOIN atable t2
ON t1.UserID = t2.UserID AND t2.IsFinal = 1
WHERE t2.UserID IS NULL;
It may so happen that they will be equally efficient in your database, but it still may be a good idea to test each of them to at least avoid ending up with one that performs worse than the others.
I think this is what you are looking for:
SELECT a.*
FROM translations a
INNER JOIN (SELECT UserID FROM translations WHERE IsFinal = 1) b
ON a.UserID = b.UserID
WHERE IsFinal = 0;
TRY ( not tested )
SELECT t1.* FROM table t1
INNER JOIN table t2 USING(indexID)
WHERE t1.IsFinal <>1
GROUP BY t1.UserID

MySQL join of 3 tables - get aggregate value from 2 child tables

I need to create a summary from 3 different tables, 1 parent table, 2 child tables.
How can I get the number of records from two child tables, based on the user id (pk in each of the 3 tables).
Parent table (user) pk is userId
Child tables 1 and 2 have composite pks of userId and webId.
I know this isn't the proper SQL syntax, but it illustrates what I'm after.
select u.userId, count(table1.webId), count(table2.webId)
from `user` u
left join `table1` t1 on u.userId = t1.userId
left join `table2` t2 on u.userId = t2.userId
group by u.userId
You might need to add DISTINCT -
SELECT u.userId, COUNT(DISTINCT table1.webId), COUNT(DISTINCT table2.webId)
FROM `user` u
LEFT JOIN `table1` t1
ON u.userId = t1.userId
LEFT JOIN `table2` t2
ON u.userId = t2.userId
GROUP BY u.userId

mysql inner join on multiple select statements

Trying to do an inner join on two composite tables, having trouble with the syntax. Here's what I have:
SELECT
count(*)
FROM
(
SELECT DISTINCT seller FROM Items, Users WHERE Items.seller = Users.userID t1
INNER JOIN
(
SELECT DISTINCT UserID FROM Bids, Users WHERE Bids.UserID = Users.userID
)
t2 ON t1.userID = t2.userID
)
I'm guessing it has something to do with the parantheses/lack of as/or whatever. I guess what I'm really asking here is how to give my subqueries aliases, but not using as in the FROM part. Is it valid just to have t1 after User.userID and identify that whole table as t1?
I think this is what you want?
SELECT count(*)
FROM Users
INNER JOIN Items ON Users.userID = Items.seller
INNER JOIN Bids ON Users.UserID = Bids.UserID
You want to name the output table which you get from query
SELECT DISTINCT seller FROM Items, Users WHERE Items.seller = Users.userID
as t1
simple way is use
`select * from (SELECT DISTINCT seller FROM Items, Users WHERE Items.seller = Users.userID)t1`