Как определить собственный заказ ORDER BY в mySQL


148

Как в MySQL определить собственный порядок сортировки.

Чтобы попытаться объяснить, что я хочу, рассмотрим эту таблицу:

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

здесь я хочу вернуть все строки, отсортированные по языку и идентификатору по возрастанию, чтобы сначала было Language = ENU, затем JPN и, наконец, DAN.

Результат должен быть: a, d, b, e, c, f и т. Д.

Это вообще возможно?

Ответы:


284

В MySQL есть удобная функция, FIELD()которая отлично подходит для таких задач.

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

Однако обратите внимание, что

  1. Это делает ваш SQL менее переносимым, так как другие СУБД могут не иметь такой функции.

  2. Когда ваш список языков (или других значений для сортировки) становится намного длиннее, лучше иметь для них отдельную таблицу со столбцом sortorder и присоединять ее к вашим запросам для упорядочивания.


3
Спасибо, это идеально подходит для моей ситуации, когда мне просто нужно упорядочить по двум значениям (основной язык, например JPN, и резервный язык, например ENU).
Muleskinner

5
Чувак, ты только что сохранил для меня переписывание в magento :)
Эрик Саймоник

1
Что делать, если у вас есть GROUP BYраньше? Например, первое значение, которое я хочу, отображается в конце?
Pathros

Поместите запрос с помощью GROUP BYв подзапрос и упорядочите его во внешнем запросе
Mchl

1
Работайте как шарм :)
Брейн

55

Если только эти три значения, то вы можете использовать в CASEвыражение :

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

(Если могут быть другие значения, вы можете добавить некоторую дополнительную логику, чтобы упорядочить порядок; например, вы можете добавить ELSE 4к этому CASEвыражению, а затем упорядочить его Languageкак третий критерий упорядочения:

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

)


1
И если есть много значений Language, у вас может быть отдельная таблица, в которой хранится каждый язык, плюс столбец порядка сортировки и ссылка на него
kaj

1
Сначала это будет упорядочено по идентификатору, в результате чего получится a, b, c, d, e, f
piotrm

Спасибо, это отлично работает - как и ответ Mchl, который я принял, поскольку он выглядит проще
Muleskinner

19

У вас есть несколько вариантов, во-первых, изменить Language на ENUM (при условии, что это возможно, и вы ожидаете только несколько вариантов)

Если вы укажете это как, ENUM('ENU','JPN','DAN')то ORDER Language ASCбудет заказывать в указанном вами порядке.

Второй будет связан с чем-то, т.е.

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

С точки зрения производительности метод ENUM будет возвращать более быстрые результаты, но доставит больше хлопот, если вам нужно добавить больше языков. Третий вариант - добавить таблицу нормализации для языков, однако в данном случае это может оказаться излишним.


Где именно вы печатаете ENUM('ENU','JPN','DAN')?
Pathros

1
@pathros в определении таблицы, вы указываете его как ENUM вместо VARCHAR и т. д. Внутренне MySQL хранит параметры ENUM в определенном порядке и индексирует их, поэтому при упорядочивании по столбцу ENUM он будет использовать этот внутренний индекс вместо строковые значения (если только CAST () не используется для возврата к VARCHAR)
Саймон на портале My School Portal

Не должно END DESC,быть END CASE DESC,?
Istiaque Ahmed

Неа. Не все CASEнужно END CASE, это зависит от контекста. CASEв ПРОЦЕДУРЕ требуется END CASE( dev.mysql.com/doc/refman/5.5/en/case.html ), однако CASEв SELECT не требуется END CASE, просто END( dev.mysql.com/doc/refman/5.7/en/… ) - в этом контекст это функция потока управления.
Саймон на портале моей школы

2

Для структуры Yii2 мы можем добиться следующего:

Project::find()
->orderBy([new Expression('FIELD(pid_is_t_m,2,0,1)'),'task_last_work'=> SORT_ASC])
->all();
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.