ORDENAR POR la lista de valores IN
Tengo una consulta SQL simple en PostgreSQL 8.3 que capta muchos comentarios. Proporciono una lista ordenada de valores a la IN
construcción en la WHERE
cláusula:
SELECT * FROM comments WHERE (comments.id IN (1,3,2,4));
Esto devuelve comentarios en un orden arbitrario que en mi caso son identificadores como 1,2,3,4
.
Quiero que las filas resultantes estén ordenadas como la lista en la IN
construcción: (1,3,2,4)
.
¿Cómo lograr eso?
Puede hacerlo con bastante facilidad con (introducido en PostgreSQL 8.2) VALORES (), ().
La sintaxis será así:
select c.*
from comments c
join (
values
(1,1),
(3,2),
(2,3),
(4,4)
) as x (id, ordering) on c.id = x.id
order by x.ordering
Úselo WITH ORDINALITY
en Postgres 9.4 o posterior.
SELECT c.*
FROM comments c
JOIN unnest('{1,3,2,4}'::int[]) WITH ORDINALITY t(id, ord) USING (id)
ORDER BY t.ord;
No es necesaria una subconsulta, podemos usar la función de retorno de conjuntos directamente como una tabla, también conocida como "función de tabla".
Un literal de cadena para pasar la matriz en lugar de un constructor ARRAY puede ser más fácil de implementar con algunos clientes.
Por conveniencia (opcionalmente), haga coincidir el nombre de la columna a la que nos estamos uniendo ("id" en el ejemplo), para que podamos unirnos con una
USING
cláusula corta y solo obtener una única instancia de la columna de unión en el resultado.Funciona con cualquier tipo de entrada. Si su columna clave es del tipo
text
, proporcione algo como'{foo,bar,baz}'::text[]
.
Explicación detallada:
- PostgreSQL unnest() con número de elemento
Simplemente porque es muy difícil de encontrar y hay que difundirlo: en mySQL esto se puede hacer mucho más sencillo , pero no sé si funciona en otros SQL.
SELECT * FROM `comments`
WHERE `comments`.`id` IN ('12','5','3','17')
ORDER BY FIELD(`comments`.`id`,'12','5','3','17')
Con Postgres 9.4 esto se puede hacer un poco más corto:
select c.*
from comments c
join (
select *
from unnest(array[43,47,42]) with ordinality
) as x (id, ordering) on c.id = x.id
order by x.ordering;
O un poco más compacto sin una tabla derivada:
select c.*
from comments c
join unnest(array[43,47,42]) with ordinality as x (id, ordering)
on c.id = x.id
order by x.ordering
Eliminando la necesidad de asignar/mantener manualmente una posición a cada valor.
Con Postgres 9.6 esto se puede hacer usando array_position()
:
with x (id_list) as (
values (array[42,48,43])
)
select c.*
from comments c, x
where id = any (x.id_list)
order by array_position(x.id_list, c.id);
El CTE se utiliza para que la lista de valores solo sea necesario especificar una vez. Si eso no es importante esto también se puede escribir como:
select c.*
from comments c
where id in (42,48,43)
order by array_position(array[42,48,43], c.id);
Creo que así es mejor:
SELECT * FROM "comments" WHERE ("comments"."id" IN (1,3,2,4))
ORDER BY id=1 DESC, id=3 DESC, id=2 DESC, id=4 DESC