Cómo definir un pedido ORDER BY personalizado en mySQL

143

En MySQL, ¿cómo defino un orden de clasificación personalizado?

Para tratar de explicar lo que quiero, considere esta tabla:

ID  Language    Text
0   ENU         a
0   JPN         b
0   DAN         c       
1   ENU         d
1   JPN         e
1   DAN         f
2   etc...

aquí quiero devolver todas las filas ordenadas por idioma e ID ascendente para que Language = ENU aparezca primero, luego JPN y finalmente DAN.

El resultado debería ser: a, d, b, e, c, f etc.

¿Es esto posible?

Muleskinner
fuente

Respuestas:

276

MySQL tiene una función útil llamada FIELD()que es excelente para tareas como esta.

ORDER BY FIELD(Language,'ENU','JPN','DAN'), ID

Tenga en cuenta sin embargo, que

  1. Hace que su SQL sea menos portátil, ya que otros DBMS podrían no tener dicha función

  2. Cuando su lista de idiomas (u otros valores para ordenar) se hace mucho más larga, es mejor tener una tabla separada con una columna de orden para ellos, y unirla a sus consultas para ordenar.

Mchl
fuente
3
Gracias, esto es perfecto para mi situación en la que solo tengo que ordenar por dos valores (un idioma principal, por ejemplo, JPN, y un idioma alternativo, por ejemplo, ENU).
Muleskinner
44
Hombre, me acabas de salvar una reescritura en magento :)
Erik Simonic
1
¿Qué pasa si tienes un GROUP BYantes? Por ejemplo, el primer valor que quiero aparece al final.
Pathros
Coloque la consulta GROUP BYen una subconsulta y
ordénela
1
Trabajar como un encanto :)
Brane
53

Si esos son los únicos tres valores, puede usar una CASEexpresión :

ORDER BY `ID`,
         CASE `Language`
         WHEN 'ENU' THEN 1
         WHEN 'JPN' THEN 2
         WHEN 'DAN' THEN 3
         END

(Si puede haber otros valores, puede que desee agregar un poco de lógica adicional para mantener el orden consistente; por ejemplo, puede agregar ELSE 4a esa CASEexpresión y luego ordenar por Languagesí mismo como el tercer criterio de orden:

ORDER BY `ID`,
         CASE `Language`
         WHEN 'ENU' THEN 1
         WHEN 'JPN' THEN 2
         WHEN 'DAN' THEN 3
         ELSE 4
         END,
         `Language`

)

ruakh
fuente
1
Y si hay muchos valores de Idioma, podría tener una tabla separada para almacenar cada Idioma más una columna de orden de clasificación y un enlace a eso
kaj
1
Esto se ordenará primero por ID, lo que dará como resultado a, b, c, d, e, f
piotrm
Gracias, esto funciona perfectamente, al igual que Mchl respondió que acepté, ya que parece más simple
Muleskinner
19

Tiene un par de opciones de antemano, la primera es cambiar el idioma para que sea ENUM (suponiendo que esto sea posible, y solo espera algunas variaciones)

Si especifica como ENUM('ENU','JPN','DAN')se ORDER Language ASCordenará en el orden que especifique.

El segundo involucrará un caso en algún lugar, es decir

SELECT * FROM table
ORDER BY CASE Language
    WHEN 'ENU' THEN 3
    WHEN 'JPN' THEN 2
    WHEN 'DAN' THEN 1
    ELSE 0
END DESC, ID ASC

En cuanto al rendimiento, el método ENUM arrojará resultados más rápidos, pero será más complicado si necesita agregar más idiomas. Una tercera opción sería agregar una tabla de normalización para los idiomas, sin embargo, puede ser excesivo en este caso.

Simon en el portal de mi escuela
fuente
¿Dónde escribes exactamente ENUM('ENU','JPN','DAN')?
Pathros
1
@pathros en la definición de la tabla, usted lo especifica como un ENUM en lugar de VARCHAR, etc. Internamente MySQL almacena las opciones de ENUM en un orden específico y las indexa, por lo que cuando ordena específicamente por una columna ENUM usará ese índice interno en lugar del valores de cadena (a menos que se use CAST () para volver a VARCHAR)
Simon en el portal de mi escuela el
No debe END DESC,ser END CASE DESC,?
Istiaque Ahmed
No No todo es CASEnecesario END CASE, depende del contexto. CASEdentro de PROCEDIMIENTO requiere END CASE( dev.mysql.com/doc/refman/5.5/en/case.html ) sin embargo, CASEdentro de SELECCIONAR no requiere END CASE, simplemente END( dev.mysql.com/doc/refman/5.7/en/… ) - en este contexto es una función de control de flujo.
Simon en el portal de mi escuela
1

Para el marco Yii2 podemos lograrlo de la siguiente manera

Project::find()
->orderBy([new Expression('FIELD(pid_is_t_m,2,0,1)'),'task_last_work'=> SORT_ASC])
->all();
Prahlad
fuente