Reputation: 10246
Query:
select id from users where id in (1,2,3,4,5)
If the users table contains ids 1, 2, 3, this would return 1, 2, and 3. I want a query that would return 4 and 5. In other words, I don't want the query to return any rows that exist in the table, I want to give it a list of numbers and get the values from that list that don't appear in the table.
(updated to clarify question following several inapplicable answers)
Upvotes: 43
Views: 44116
Reputation: 31
select missing.id
from
(select ELT(@indx, 1,2,3,4,5) as id, @indx:=@indx+1
from (select @indx:=1) init,
users
where ELT(@indx, 1,2,3,4,5) is not null
) missing
left join users u using(id)
where u.id is null;
What you have in here is:
ELT
together with the variable @indx
allows you to 'transpose' the list into a column. (select @indx:=1)
is needed to initialize indx to 1 users
table in the inner select is needed so that MySQL has something to iterate on (so the size of your list cannot exceed the number of the rows in users table, if that's the case that you can use any other table that is big enough instead of inner users, again, table itself does not matter it's just to have something to iterate on, so only its size that matters).ELT(@indx, 1,2,3,4,5) is not null
condition in the nested select is to stop iteration once you are at the index exceeding your list size.The rest is simple - left join
and check for null
.
Upvotes: 3
Reputation: 17858
Given the numbers are a fixed list. Quickest way I can think of is have a test table, populated with those numbers and do
untested select statement - but you will follow the princpal.
select test.number
from test
left join
users
on
test.number = users.id
where test.number <> users.id
Then you'll get back all the numbers that dont have a matching user.id and so can fill in the holes..
Upvotes: 16
Reputation: 55
Sorry, I cannot add comments, @Austin,
You have my +1.
Anyway, not sure if this works on mysql, but change all that atomic selects concatenated unions for a value set, so you have something like that:
SELECT id FROM (
VALUES (1), (2), (3), (4), (5)
) AS list(id)
LEFT JOIN users USING (id)
WHERE users.id IS NULL
Upvotes: -2
Reputation: 3328
If you don't want to (explicitly) use temporary tables, this will work:
SELECT id FROM (
(SELECT 1 AS id) UNION ALL
(SELECT 2 AS id) UNION ALL
(SELECT 3 AS id) UNION ALL
(SELECT 4 AS id) UNION ALL
(SELECT 5 AS id)
) AS list
LEFT JOIN users USING (id)
WHERE users.id IS NULL
However, it is quite ugly, quite long, and I am dubious about how it would perform if the list of IDs is long.
Upvotes: 22
Reputation: 3854
Had the same need and built on the answer by BugFinder using a temporary table in session. This way it will automatically be destroyed after I'm done with the query, so I don't have to deal with house cleaning as I will run this type of query often.
Create the temporary table:
CREATE TEMPORARY TABLE tmp_table (id INT UNSIGNED);
Populate tmp_table with the values you will check:
INSERT INTO tmp_table (id) values (1),(2),(3),(4),(5);
With the table created and populated, run the query as with any regular table:
SELECT tmp_table.id
FROM tmp_table
LEFT JOIN users u
ON tmp_table.id = u.id
WHERE u.id IS NULL;
This info on MySQL Temporary Tables was also useful
Upvotes: 17
Reputation: 2924
A different option is to use another table containing all possible ids and then do a select from there:
mysql> describe ids;
+-------+-----------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-----------------+------+-----+---------+-------+
| id | int(5) unsigned | NO | | 0 | |
+-------+-----------------+------+-----+---------+-------+
1 row in set (0.05 sec)
mysql> select * from ids;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
+----+
5 rows in set (0.00 sec)
mysql> select * from users;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
+----+
3 rows in set (0.00 sec)
mysql> select id from ids where id not in (select id from users);
+----+
| id |
+----+
| 4 |
| 5 |
+----+
2 rows in set (0.04 sec)
Added side effect - allows you to expand the result list by inserting into the ids table
Upvotes: 3