Jak zdefiniować niestandardowe zamówienie ORDER BY w mySQL

143

W MySQL, jak zdefiniować niestandardową kolejność sortowania.

Aby spróbować wyjaśnić, czego chcę, rozważ poniższą tabelę:

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

tutaj chcę zwrócić wszystkie wiersze posortowane według języka i rosnącego identyfikatora, tak aby język = ENU był pierwszy, następnie JPN i na końcu DAN.

Wynik powinien być: a, d, b, e, c, f itd.

Czy to w ogóle możliwe?

Muleskinner
źródło

Odpowiedzi:

276

MySQL ma przydatną funkcję o nazwie, FIELD()która doskonale nadaje się do takich zadań.

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

Pamiętaj jednak, że

  1. To sprawia, że ​​twój SQL jest mniej przenośny, ponieważ inne DBMS mogą nie mieć takiej funkcji

  2. Kiedy lista języków (lub innych wartości do sortowania) znacznie się wydłuża, lepiej mieć dla nich oddzielną tabelę z kolumną sortowania i dołączyć ją do zapytań w celu uporządkowania.

Mchl
źródło
3
Dzięki temu jest to idealne rozwiązanie dla mojej sytuacji, w której wystarczy uporządkować według dwóch wartości (język podstawowy, na przykład JPN, i język rezerwowy, na przykład ENU).
Muleskinner,
4
Człowieku, właśnie uratowałeś mi przepisanie w magento :)
Erik Simonic
1
A co jeśli masz GROUP BYprzed? Na przykład pierwsza wartość, którą chcę, pojawia się na końcu?
Pathros
Umieść zapytanie GROUP BYw podzapytaniu i uporządkuj je w zapytaniu zewnętrznym
Mchl
1
Działa jak urok :)
Brane
53

Jeśli są to tylko trzy wartości, a następnie można użyć do CASEwyrażenia :

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

(Jeśli mogą istnieć inne wartości, możesz dodać dodatkową logikę, aby zachować spójność kolejności; na przykład możesz dodać ELSE 4do tego CASEwyrażenia, a następnie ustawić Languagesamodzielnie jako trzecie kryterium kolejności:

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

)

ruakh
źródło
1
A jeśli istnieje wiele wartości językowych, możesz mieć osobną tabelę przechowującą każdy język oraz kolumnę porządku sortowania i link do niej
kaj,
1
Najpierw zostanie uporządkowane według identyfikatora, co spowoduje a, b, c, d, e, f
piotrm
Dzięki temu działa idealnie - podobnie jak odpowiedź Mchla, którą zaakceptowałem, ponieważ wygląda na prostszą
Muleskinner
19

Masz kilka opcji od ręki, pierwszą jest zmiana języka na ENUM (zakładając, że jest to możliwe i oczekujesz tylko kilku odmian)

Jeśli określisz to jako, ENUM('ENU','JPN','DAN')to ORDER Language ASCuporządkuje w określonej kolejności.

Drugi będzie dotyczył sprawy gdzieś, tj

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

Pod względem wydajności metoda ENUM zwróci szybsze wyniki, ale będzie bardziej kłopotliwa, jeśli musisz dodać więcej języków. Trzecią opcją byłoby dodanie tabeli normalizacji dla języków, jednak w tym przypadku może to być przesada.

Simon na portalu My School
źródło
Gdzie dokładnie piszesz ENUM('ENU','JPN','DAN')?
Pathros
1
@pathros w definicji tabeli, określasz ją jako ENUM zamiast VARCHAR itp. Wewnętrznie MySQL przechowuje opcje ENUM w określonej kolejności i indeksuje je, więc przy porządkowaniu według kolumny ENUM użyje tego wewnętrznego indeksu zamiast wartości ciągów (chyba, że ​​CAST () jest używany do przywrócenia go do VARCHAR)
Simon at My School Portal
Nie powinno END DESC,być END CASE DESC,?
Istiaque Ahmed
Nie. Nie wszyscy CASEpotrzebują END CASE, to zależy od kontekstu. CASEw ramach PROCEDURE wymagają END CASE( dev.mysql.com/doc/refman/5.5/en/case.html ) jednak CASEw ramach SELECT nie wymaga END CASE, po prostu END( dev.mysql.com/doc/refman/5.7/en/ ... ) - w tym kontekst to funkcja sterowania przepływem.
Simon at My School Portal
1

W przypadku frameworka Yii2 osiągamy to poprzez podążanie drogą

Project::find()
->orderBy([new Expression('FIELD(pid_is_t_m,2,0,1)'),'task_last_work'=> SORT_ASC])
->all();
Prahlad
źródło