SELECT that returns list of values not occurring in any row

后端 未结 6 1570
星月不相逢
星月不相逢 2020-12-14 06:02

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

相关标签:
6条回答
  • 2020-12-14 06:33

    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

    0 讨论(0)
  • 2020-12-14 06:39

    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

    0 讨论(0)
  • 2020-12-14 06:46

    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
    
    0 讨论(0)
  • 2020-12-14 06:50

    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..

    0 讨论(0)
  • 2020-12-14 06:57
    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
    • and 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.

    0 讨论(0)
  • 2020-12-14 07:00

    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.

    0 讨论(0)
提交回复
热议问题