Comment définir une commande ORDER BY personnalisée dans mySQL

143

Dans MySQL, comment définir un ordre de tri personnalisé.

Pour essayer d'expliquer ce que je veux, considérez ce tableau:

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

ici, je veux retourner toutes les lignes triées par langue et par ID croissant afin que Language = ENU vienne en premier, puis JPN et enfin DAN.

Le résultat doit être: a, d, b, e, c, f etc.

Est-ce seulement possible?

Muleskinner
la source

Réponses:

276

MySQL a une fonction pratique appelée FIELD()qui est excellente pour des tâches comme celle-ci.

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

Notez cependant que

  1. Cela rend votre SQL moins portable, car d'autres SGBD peuvent ne pas avoir une telle fonction

  2. Lorsque votre liste de langues (ou d'autres valeurs sur lesquelles trier) s'allonge beaucoup, il est préférable d'avoir une table séparée avec une colonne de tri pour elles et de la joindre à vos requêtes pour les classer.

Mchl
la source
3
Merci c'est parfaitement pour ma situation où je dois juste commander par deux valeurs (une langue principale, par exemple JPN, et une langue de secours, par exemple ENU).
Muleskinner
4
Mec, tu viens de me sauver une réécriture dans magento :)
Erik Simonic
1
Et si vous avez un GROUP BYavant? Par exemple, la première valeur que je veux, apparaît à la fin?
Pathros
Placer la requête avec GROUP BYdans une sous-requête et la
classer
1
Travaillez comme un charme :)
Brane
53

Si ce sont les trois seules valeurs, vous pouvez utiliser une CASEexpression :

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

(S'il peut y avoir d'autres valeurs, alors vous pouvez ajouter une logique supplémentaire pour garder l'ordre cohérent; par exemple, vous pouvez ajouter ELSE 4à cette CASEexpression, puis commander par Languagelui-même comme troisième critère de classement:

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

)

Ruakh
la source
1
Et s'il y a beaucoup de valeurs de langue, vous pouvez avoir une table séparée stockant chaque langue plus une colonne d'ordre de tri et un lien vers cela
kaj
1
Cela sera trié par ID en premier, résultant en a, b, c, d, e, f
piotrm
Merci, cela fonctionne parfaitement - tout comme la réponse de Mchl que j'ai acceptée car elle semble plus simple
Muleskinner
19

Vous avez quelques options, la première est de changer la langue en ENUM (en supposant que cela soit possible, et vous ne vous attendez qu'à quelques variations)

Si vous le spécifiez comme ENUM('ENU','JPN','DAN')alors ORDER Language ASC, l'ordre dans l'ordre que vous spécifiez.

La seconde impliquera un cas quelque part, c'est-à-dire

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 termes de performances, la méthode ENUM renverra des résultats plus rapides, mais sera plus compliquée si vous avez besoin d'ajouter plus de langues. Une troisième option serait d'ajouter une table de normalisation pour les langues, mais cela peut être excessif dans ce cas.

Simon au portail de mon école
la source
Où tapez-vous exactement ENUM('ENU','JPN','DAN')?
Pathros
1
@pathros dans la définition de la table, vous le spécifiez comme ENUM au lieu de VARCHAR etc. En interne, MySQL stocke les options ENUM dans un ordre spécifique et les indexe. valeurs de chaîne (sauf si CAST () est utilisé pour revenir à un VARCHAR)
Simon at My School Portal
Ne devrait pas l' END DESC,être END CASE DESC,?
Istiaque Ahmed
Nan. Tous CASEn'ont pas besoin END CASE, cela dépend du contexte. CASEdans PROCEDURE require END CASE( dev.mysql.com/doc/refman/5.5/en/case.html ) mais CASEdans SELECT ne nécessite pas END CASE, simplement END( dev.mysql.com/doc/refman/5.7/en/… ) - dans ce contexte c'est une fonction de flux de contrôle.
Simon au portail de mon école
1

Pour le framework Yii2, nous pouvons réaliser de la manière suivante

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