[mysql] Ordering by the order of values in a SQL IN() clause

I am wondering if there is away (possibly a better way) to order by the order of the values in an IN() clause.

The problem is that I have 2 queries, one that gets all of the IDs and the second that retrieves all the information. The first creates the order of the IDs which I want the second to order by. The IDs are put in an IN() clause in the correct order.

So it'd be something like (extremely simplified):

SELECT id FROM table1 WHERE ... ORDER BY display_order, name

SELECT name, description, ... WHERE id IN ([id's from first])

The issue is that the second query does not return the results in the same order that the IDs are put into the IN() clause.

One solution I have found is to put all of the IDs into a temp table with an auto incrementing field which is then joined into the second query.

Is there a better option?

Note: As the first query is run "by the user" and the second is run in a background process, there is no way to combine the 2 into 1 query using sub queries.

I am using MySQL, but I'm thinking it might be useful to have it noted what options there are for other DBs as well.

This question is related to mysql sql sql-order-by

The answer is


The IN clause describes a set of values, and sets do not have order.

Your solution with a join and then ordering on the display_order column is the most nearly correct solution; anything else is probably a DBMS-specific hack (or is doing some stuff with the OLAP functions in standard SQL). Certainly, the join is the most nearly portable solution (though generating the data with the display_order values may be problematic). Note that you may need to select the ordering columns; that used to be a requirement in standard SQL, though I believe it was relaxed as a rule a while ago (maybe as long ago as SQL-92).


If you want to do arbitrary sorting on a query using values inputted by the query in MS SQL Server 2008+, it can be done by creating a table on the fly and doing a join like so (using nomenclature from OP).

SELECT table1.name, table1.description ... 
FROM (VALUES (id1,1), (id2,2), (id3,3) ...) AS orderTbl(orderKey, orderIdx) 
LEFT JOIN table1 ON orderTbl.orderKey=table1.id
ORDER BY orderTbl.orderIdx

If you replace the VALUES statement with something else that does the same thing, but in ANSI SQL, then this should work on any SQL database.

Note: The second column in the created table (orderTbl.orderIdx) is necessary when querying record sets larger than 100 or so. I originally didn't have an orderIdx column, but found that with result sets larger than 100 I had to explicitly sort by that column; in SQL Server Express 2014 anyways.


My first thought was to write a single query, but you said that was not possible because one is run by the user and the other is run in the background. How are you storing the list of ids to pass from the user to the background process? Why not put them in a temporary table with a column to signify the order.

So how about this:

  1. The user interface bit runs and inserts values into a new table you create. It would insert the id, position and some sort of job number identifier)
  2. The job number is passed to the background process (instead of all the ids)
  3. The background process does a select from the table in step 1 and you join in to get the other information that you require. It uses the job number in the WHERE clause and orders by the position column.
  4. The background process, when finished, deletes from the table based on the job identifier.

Two solutions that spring to mind:

  1. order by case id when 123 then 1 when 456 then 2 else null end asc

  2. order by instr(','||id||',',',123,456,') asc

(instr() is from Oracle; maybe you have locate() or charindex() or something like that)


SELECT ORDER_NO, DELIVERY_ADDRESS 
from IFSAPP.PURCHASE_ORDER_TAB 
where ORDER_NO in ('52000077','52000079','52000167','52000297','52000204','52000409','52000126') 
ORDER BY instr('52000077,52000079,52000167,52000297,52000204,52000409,52000126',ORDER_NO)

worked really great


Two solutions that spring to mind:

  1. order by case id when 123 then 1 when 456 then 2 else null end asc

  2. order by instr(','||id||',',',123,456,') asc

(instr() is from Oracle; maybe you have locate() or charindex() or something like that)


The IN clause describes a set of values, and sets do not have order.

Your solution with a join and then ordering on the display_order column is the most nearly correct solution; anything else is probably a DBMS-specific hack (or is doing some stuff with the OLAP functions in standard SQL). Certainly, the join is the most nearly portable solution (though generating the data with the display_order values may be problematic). Note that you may need to select the ordering columns; that used to be a requirement in standard SQL, though I believe it was relaxed as a rule a while ago (maybe as long ago as SQL-92).


Two solutions that spring to mind:

  1. order by case id when 123 then 1 when 456 then 2 else null end asc

  2. order by instr(','||id||',',',123,456,') asc

(instr() is from Oracle; maybe you have locate() or charindex() or something like that)


Ans to get sorted data.

SELECT ...
FROM ...
ORDER  BY FIELD(user_id,5,3,2,...,50)  LIMIT 10

I think you should manage to store your data in a way that you will simply do a join and it will be perfect, so no hacks and complicated things going on.

I have for instance a "Recently played" list of track ids, on SQLite i simply do:

SELECT * FROM recently NATURAL JOIN tracks;

I just tried to do this is MS SQL Server where we do not have FIELD():

SELECT table1.id
... 
INNER JOIN
    (VALUES (10,1),(3,2),(4,3),(5,4),(7,5),(8,6),(9,7),(2,8),(6,9),(5,10)
    ) AS X(id,sortorder)
        ON X.id = table1.id
    ORDER BY X.sortorder

Note that I am allowing duplication too.


SELECT ORDER_NO, DELIVERY_ADDRESS 
from IFSAPP.PURCHASE_ORDER_TAB 
where ORDER_NO in ('52000077','52000079','52000167','52000297','52000204','52000409','52000126') 
ORDER BY instr('52000077,52000079,52000167,52000297,52000204,52000409,52000126',ORDER_NO)

worked really great


Give this a shot:

SELECT name, description, ...
WHERE id IN
    (SELECT id FROM table1 WHERE...)
ORDER BY
    (SELECT display_order FROM table1 WHERE...),
    (SELECT name FROM table1 WHERE...)

The WHEREs will probably take a little tweaking to get the correlated subqueries working properly, but the basic principle should be sound.


Ans to get sorted data.

SELECT ...
FROM ...
ORDER  BY FIELD(user_id,5,3,2,...,50)  LIMIT 10

See following how to get sorted data.

SELECT ...
  FROM ...
 WHERE zip IN (91709,92886,92807,...,91356)
   AND user.status=1
ORDER 
    BY provider.package_id DESC 
     , FIELD(zip,91709,92886,92807,...,91356)
LIMIT 10

I think you should manage to store your data in a way that you will simply do a join and it will be perfect, so no hacks and complicated things going on.

I have for instance a "Recently played" list of track ids, on SQLite i simply do:

SELECT * FROM recently NATURAL JOIN tracks;

The IN clause describes a set of values, and sets do not have order.

Your solution with a join and then ordering on the display_order column is the most nearly correct solution; anything else is probably a DBMS-specific hack (or is doing some stuff with the OLAP functions in standard SQL). Certainly, the join is the most nearly portable solution (though generating the data with the display_order values may be problematic). Note that you may need to select the ordering columns; that used to be a requirement in standard SQL, though I believe it was relaxed as a rule a while ago (maybe as long ago as SQL-92).


I just tried to do this is MS SQL Server where we do not have FIELD():

SELECT table1.id
... 
INNER JOIN
    (VALUES (10,1),(3,2),(4,3),(5,4),(7,5),(8,6),(9,7),(2,8),(6,9),(5,10)
    ) AS X(id,sortorder)
        ON X.id = table1.id
    ORDER BY X.sortorder

Note that I am allowing duplication too.


The IN clause describes a set of values, and sets do not have order.

Your solution with a join and then ordering on the display_order column is the most nearly correct solution; anything else is probably a DBMS-specific hack (or is doing some stuff with the OLAP functions in standard SQL). Certainly, the join is the most nearly portable solution (though generating the data with the display_order values may be problematic). Note that you may need to select the ordering columns; that used to be a requirement in standard SQL, though I believe it was relaxed as a rule a while ago (maybe as long ago as SQL-92).


If you want to do arbitrary sorting on a query using values inputted by the query in MS SQL Server 2008+, it can be done by creating a table on the fly and doing a join like so (using nomenclature from OP).

SELECT table1.name, table1.description ... 
FROM (VALUES (id1,1), (id2,2), (id3,3) ...) AS orderTbl(orderKey, orderIdx) 
LEFT JOIN table1 ON orderTbl.orderKey=table1.id
ORDER BY orderTbl.orderIdx

If you replace the VALUES statement with something else that does the same thing, but in ANSI SQL, then this should work on any SQL database.

Note: The second column in the created table (orderTbl.orderIdx) is necessary when querying record sets larger than 100 or so. I originally didn't have an orderIdx column, but found that with result sets larger than 100 I had to explicitly sort by that column; in SQL Server Express 2014 anyways.


My first thought was to write a single query, but you said that was not possible because one is run by the user and the other is run in the background. How are you storing the list of ids to pass from the user to the background process? Why not put them in a temporary table with a column to signify the order.

So how about this:

  1. The user interface bit runs and inserts values into a new table you create. It would insert the id, position and some sort of job number identifier)
  2. The job number is passed to the background process (instead of all the ids)
  3. The background process does a select from the table in step 1 and you join in to get the other information that you require. It uses the job number in the WHERE clause and orders by the position column.
  4. The background process, when finished, deletes from the table based on the job identifier.

Give this a shot:

SELECT name, description, ...
WHERE id IN
    (SELECT id FROM table1 WHERE...)
ORDER BY
    (SELECT display_order FROM table1 WHERE...),
    (SELECT name FROM table1 WHERE...)

The WHEREs will probably take a little tweaking to get the correlated subqueries working properly, but the basic principle should be sound.


See following how to get sorted data.

SELECT ...
  FROM ...
 WHERE zip IN (91709,92886,92807,...,91356)
   AND user.status=1
ORDER 
    BY provider.package_id DESC 
     , FIELD(zip,91709,92886,92807,...,91356)
LIMIT 10

For Oracle, John's solution using instr() function works. Here's slightly different solution that worked - SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)


Use MySQL FIND_IN_SET function:

  SELECT * 
    FROM table_name 
   WHERE id IN (..,..,..,..) 
ORDER BY FIND_IN_SET (coloumn_name, .., .., ..);

Two solutions that spring to mind:

  1. order by case id when 123 then 1 when 456 then 2 else null end asc

  2. order by instr(','||id||',',',123,456,') asc

(instr() is from Oracle; maybe you have locate() or charindex() or something like that)


Use MySQL FIND_IN_SET function:

  SELECT * 
    FROM table_name 
   WHERE id IN (..,..,..,..) 
ORDER BY FIND_IN_SET (coloumn_name, .., .., ..);

Give this a shot:

SELECT name, description, ...
WHERE id IN
    (SELECT id FROM table1 WHERE...)
ORDER BY
    (SELECT display_order FROM table1 WHERE...),
    (SELECT name FROM table1 WHERE...)

The WHEREs will probably take a little tweaking to get the correlated subqueries working properly, but the basic principle should be sound.


For Oracle, John's solution using instr() function works. Here's slightly different solution that worked - SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)


My first thought was to write a single query, but you said that was not possible because one is run by the user and the other is run in the background. How are you storing the list of ids to pass from the user to the background process? Why not put them in a temporary table with a column to signify the order.

So how about this:

  1. The user interface bit runs and inserts values into a new table you create. It would insert the id, position and some sort of job number identifier)
  2. The job number is passed to the background process (instead of all the ids)
  3. The background process does a select from the table in step 1 and you join in to get the other information that you require. It uses the job number in the WHERE clause and orders by the position column.
  4. The background process, when finished, deletes from the table based on the job identifier.

Examples related to mysql

Implement specialization in ER diagram How to post query parameters with Axios? PHP with MySQL 8.0+ error: The server requested authentication method unknown to the client Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver' phpMyAdmin - Error > Incorrect format parameter? Authentication plugin 'caching_sha2_password' is not supported How to resolve Unable to load authentication plugin 'caching_sha2_password' issue Connection Java-MySql : Public Key Retrieval is not allowed How to grant all privileges to root user in MySQL 8.0 MySQL 8.0 - Client does not support authentication protocol requested by server; consider upgrading MySQL client

Examples related to sql

Passing multiple values for same variable in stored procedure SQL permissions for roles Generic XSLT Search and Replace template Access And/Or exclusions Pyspark: Filter dataframe based on multiple conditions Subtracting 1 day from a timestamp date PYODBC--Data source name not found and no default driver specified select rows in sql with latest date for each ID repeated multiple times ALTER TABLE DROP COLUMN failed because one or more objects access this column Create Local SQL Server database

Examples related to sql-order-by

Laravel Eloquent: Ordering results of all() SQL for ordering by number - 1,2,3,4 etc instead of 1,10,11,12 SQL ORDER BY multiple columns How to use Oracle ORDER BY and ROWNUM correctly? MySQL order by before group by Ordering by specific field value first MySQL ORDER BY multiple column ASC and DESC How can I get just the first row in a result set AFTER ordering? SQL order string as number Order by multiple columns with Doctrine