Ordenar por el orden de los valores en una cláusula SQL IN()


Me pregunto si existe (posiblemente una mejor manera) de ordenar por el orden de los valores en una cláusula IN ().

El problema es que tengo 2 consultas, una que obtiene todos los ID y la segunda que recupera toda la información. El primero crea el orden de los IDs que quiero que el segundo ordene. Los IDs se ponen en una cláusula IN () en el orden correcto.

Así que sería algo como (extremadamente simplificado):

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

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

El problema es que la segunda consulta no devuelve los resultados en el mismo orden que los IDs se ponen en la cláusula IN ().

Una solución que he encontrado es poner todos los ID en una tabla temporal con un campo de incremento automático que luego se une a la segunda consulta.

¿Hay una opción mejor?

Nota: Como la primera consulta se ejecuta "por el usuario" y la segunda se ejecuta en un proceso en segundo plano, no hay forma de combinar la consulta 2 en 1 usando sub consultas.

Estoy usando MySQL, pero Estoy pensando que podría ser útil tener que señalar qué opciones hay para otros DBs también.

Author: Patrick Kostjens, 2008-12-29

13 answers

Usar MySQL FIELD() función:

SELECT name, description, ...
FROM ...
WHERE id IN([ids, any order])
ORDER BY FIELD(id, [ids in order])

FIELD() devolverá el índice del primer parámetro que es igual al primer parámetro (que no sea el primer parámetro en sí).

FIELD('a', 'a', 'b', 'c')

Devolverá 1

FIELD('a', 'c', 'b', 'a')

Devolverá 3

Esto hará exactamente lo que quieras si pegas los ids en la cláusula IN() y la función FIELD() en el mismo orden.

 170
Author: ʞɔıu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-30 17:34:12

Vea a continuación cómo obtener datos ordenados.

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
 13
Author: Pradeep Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-12-01 09:34:20

Dos soluciones que vienen a la mente:

  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() es de Oracle; tal vez usted tiene locate() o charindex() o algo así)

 11
Author: John Nilsson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-29 06:57:53

Ans para obtener datos ordenados.

SELECT ...
FROM ...
ORDER  BY FIELD(user_id,5,3,2,...,50)  LIMIT 10
 5
Author: Gulshan Prajapati,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-30 09:27:47

Si desea hacer una clasificación arbitraria en una consulta utilizando los valores introducidos por la consulta en MS SQL Server 2008+, puede hacerlo creando una tabla sobre la marcha y haciendo una combinación como esta (usando nomenclatura de 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

Si reemplaza la instrucción VALUES con otra cosa que haga lo mismo, pero en ANSI SQL, entonces esto debería funcionar en cualquier base de datos SQL.

Nota: La segunda columna de la tabla creada (orderTbl.orderIdx) es necesario al realizar consultas conjuntos de registros mayores a 100 aproximadamente. Originalmente no tenía una columna orderIdx, pero encontré que con conjuntos de resultados mayores a 100 tenía que ordenar explícitamente por esa columna; en SQL Server Express 2014 de todos modos.

 5
Author: Ian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-08 22:50:44

La cláusula IN describe un conjunto de valores, y los conjuntos no tienen orden.

Su solución con una combinación y luego ordenar en la columna display_order es la solución más casi correcta; cualquier otra cosa es probablemente un truco específico de DBMS (o está haciendo algunas cosas con las funciones OLAP en SQL estándar). Ciertamente, la combinación es la solución más portátil (aunque generar los datos con los valores display_order puede ser problemático). Tenga en cuenta que es posible que tenga que seleccionar las columnas de orden; que se utiliza para ser un requisito en SQL estándar, aunque creo que fue relajado como una regla hace un tiempo (tal vez hace tanto tiempo como SQL-92).

 4
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-29 07:02:59

Para Oracle, la solución de John usando la función instr() funciona. Aquí hay una solución ligeramente diferente que funcionó - SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)

 2
Author: V Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-31 23:54:19
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)

Funcionó muy bien

 2
Author: Ravi Ranjan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-05 11:43:15

Use MySQL FIND_IN_SET función:

  SELECT * 
    FROM table_name 
   WHERE id IN (..,..,..,..) 
ORDER BY FIND_IN_SET (coloumn_name, .., .., ..);
 2
Author: Sarthak Sawhney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-21 16:34:32

Mi primer pensamiento fue escribir una sola consulta, pero dijiste que no era posible porque uno es ejecutado por el usuario y el otro se ejecuta en segundo plano. ¿Cómo está almacenando la lista de ID para pasar del usuario al proceso en segundo plano? Por qué no ponerlos en una tabla temporal con una columna para indicar el orden.

Entonces qué tal esto:

  1. El bit de interfaz de usuario se ejecuta e inserta valores en una nueva tabla que cree. Insertaría el id, la posición y algún tipo de trabajo identificador del número)
  2. El número de trabajo se pasa al proceso en segundo plano (en lugar de todos los id)
  3. El proceso de fondo hace una selección de la tabla en el paso 1 y se une para obtener la otra información que necesita. Utiliza el número de trabajo en la cláusula WHERE y ordena por la columna posición.
  4. El proceso en segundo plano, cuando finaliza, se elimina de la tabla en función del identificador del trabajo.
 1
Author: WW.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-28 23:50:39

Creo que debe manejar para almacenar sus datos de una manera que simplemente va a hacer un join y será perfecto, por lo que no hay hacks y cosas complicadas pasando.

Tengo por ejemplo una lista "Recientemente reproducida" de id de pista, en SQLite simplemente lo hago:

SELECT * FROM recently NATURAL JOIN tracks;
 1
Author: kroe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-10-23 18:32:56

Dale una oportunidad a esto:

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

Los WHEREs probablemente requerirán un pequeño ajuste para que las subconsultas correlacionadas funcionen correctamente, pero el principio básico debería ser sólido.

 0
Author: chaos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-12-28 22:17:27

Acabo de intentar hacer esto es MS SQL Server donde no tenemos 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

Tenga en cuenta que también estoy permitiendo la duplicación.

 0
Author: Tony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-16 10:13:33