Como definir uma ordem ORDER BY personalizada no mySQL

142

No MySQL, como defino uma ordem de classificação personalizada.

Para tentar explicar o que eu quero, considere esta tabela:

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

aqui eu quero retornar todas as linhas classificadas por Idioma e ID crescente, para que Idioma = ENU seja o primeiro, depois o JPN e, por fim, a DAN.

O resultado deve ser: a, d, b, e, c, f etc.

Isso é possível?

Muleskinner
fonte

Respostas:

275

O MySQL possui uma função útil chamada FIELD()que é excelente para tarefas como esta.

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

Note, no entanto, que

  1. Isso torna seu SQL menos portátil, pois outros DBMSs podem não ter essa função

  2. Quando sua lista de idiomas (ou outros valores a serem classificados) fica muito mais longa, é melhor ter uma tabela separada com uma coluna de ordem de classificação para eles e associá-la às suas consultas para pedidos.

Mchl
fonte
3
Obrigado, isso é perfeitamente para a minha situação em que eu apenas tenho que ordenar por dois valores (um idioma primário, por exemplo, JPN, e um idioma substituto, por exemplo, ENU).
Muleskinner
4
Homem que você acabou de me salvou uma reescrita em magento :)
Erik Simonic
1
E se você tiver um GROUP BYantes? Por exemplo, o primeiro valor que eu quero, aparece no final?
Pathros 25/03
Coloque a consulta com GROUP BYem uma subconsulta e solicite-a na consulta externa
Mchl
1
Trabalho como um encanto :)
Brane
53

Se esses são os únicos três valores, você pode usar uma CASEexpressão :

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

(Se houver outros valores, convém adicionar alguma lógica extra para manter a ordem consistente; por exemplo, você poderá adicionar ELSE 4essa CASEexpressão e, em seguida, ordenar por Languagesi só como o terceiro critério de ordem:

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

)

ruakh
fonte
1
E se houver muitos valores de idioma, você poderá ter uma tabela separada armazenando cada idioma, mais uma coluna de ordem de classificação e um link para isso
kaj
1
Esta ordem vontade por ID primeiro, resultando em a, b, c, d, e, f
piotrm
Obrigado, isso funciona perfeitamente - assim como Mchl respondeu, que eu aceitei, pois parece mais simples #
5800
19

Você tem algumas opções de antemão, a primeira é mudar o idioma para ENUM (supondo que isso seja possível e você só espera algumas variações)

Se você especificá-lo como ENUM('ENU','JPN','DAN'), em seguida, ORDER Language ASCirá pedir na ordem que você especificar.

O segundo envolverá um caso em algum lugar, ou seja,

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

Em termos de desempenho, o método ENUM retornará resultados mais rápidos, mas terá mais problemas se você precisar adicionar mais idiomas. Uma terceira opção seria adicionar uma tabela de normalização para os idiomas, mas isso pode ser um exagero nesse caso.

Simon no portal da minha escola
fonte
Onde exatamente você digita ENUM('ENU','JPN','DAN')?
Pathros 25/03
1
@pathros na definição da tabela, você o especifica como um ENUM em vez de VARCHAR etc. Internamente, o MySQL armazena as opções ENUM em uma ordem específica e as indexa; portanto, ao fazer o pedido por uma coluna ENUM especificamente, ele usará esse índice interno em vez de valores de cadeia (a menos que CAST () é usado para torná-lo de volta para um VARCHAR)
Simon em My Portal Escola
Não deveria END DESC,ser END CASE DESC,?
Istiaque Ahmed
Não. Nem todas as CASEnecessidades END CASE, dependem do contexto. CASEdentro de PROCEDURE require END CASE( dev.mysql.com/doc/refman/5.5/en/case.html ), porém CASEno SELECT não é necessário END CASE, simplesmente END( dev.mysql.com/doc/refman/5.7/en/… ) - neste contexto, é uma função de controle de fluxo.
Simon no portal da minha escola
1

Para a estrutura Yii2, conseguimos alcançar da seguinte maneira

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