Come definire un ordine ORDER BY personalizzato in mySQL


143

In MySQL come posso definire un ordinamento personalizzato.

Per provare a spiegare cosa voglio prendere in considerazione questa tabella:

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

qui voglio restituire tutte le righe ordinate per Lingua e ID ascendente in modo che Lingua = ENU venga prima, poi JPN e infine DAN.

Il risultato dovrebbe essere: a, d, b, e, c, f ecc.

È possibile?

Risposte:


276

MySQL ha una pratica funzione chiamata FIELD()che è eccellente per compiti come questo.

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

Si noti tuttavia che

  1. Rende il tuo SQL meno portatile, poiché altri DBMS potrebbero non avere tale funzione

  2. Quando il tuo elenco di lingue (o altri valori per cui ordinare) diventa molto più lungo, è meglio avere una tabella separata con una colonna di ordinamento per loro e unirla alle tue query per l'ordinamento.


3
Grazie, questo è perfetto per la mia situazione in cui devo solo ordinare in base a due valori (una lingua principale, ad esempio JPN, e una lingua di fallback, ad esempio ENU).
Muleskinner,

4
Amico, mi hai appena salvato una riscrittura in magento :)
Erik Simonic,

1
E se avessi un GROUP BYprima? Ad esempio, il primo valore che voglio appare alla fine?
Pathros,

Inserisci la query con GROUP BYin una query secondaria e
ordinala

1
Lavora come un fascino :)
Brane,

53

Se queste sono le uniche tre valori, quindi è possibile utilizzare un CASEespressione :

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

(Se potrebbero esserci altri valori, potresti voler aggiungere qualche logica in più per mantenere coerente l'ordinamento; ad esempio, potresti aggiungere ELSE 4a quell'espressione CASEe quindi ordinare da Languagesolo come terzo criterio di ordinamento:

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

)


1
E se ci sono molti valori di lingua, potresti avere una tabella separata che memorizza ogni lingua più una colonna di ordinamento e un link a quello
Kaj

1
Questo ordinerà prima per ID, risultando in a, b, c, d, e, f
piotrm,

Grazie, funziona perfettamente - come ha risposto Mchl, che ho accettato perché sembra più semplice
Muleskinner,

19

Hai un paio di opzioni a portata di mano, la prima è cambiare la lingua in ENUM (supponendo che sia possibile e ti aspetti solo alcune variazioni)

Se lo specifichi come ENUM('ENU','JPN','DAN')allora ORDER Language ASCordinerà nell'ordine specificato.

Il secondo coinvolgerà un caso da qualche parte, cioè

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

Per quanto riguarda le prestazioni, il metodo ENUM restituirà risultati più rapidi, ma è più problematico se è necessario aggiungere più lingue. Una terza opzione sarebbe quella di aggiungere una tabella di normalizzazione per le lingue, tuttavia in questo caso potrebbe essere eccessivo.


Dove scrivi esattamente ENUM('ENU','JPN','DAN')?
Pathros,

1
@pathros nella definizione della tabella, lo si specifica come ENUM anziché VARCHAR, ecc. Internamente MySQL memorizza le opzioni ENUM in un ordine specifico e le indicizza, quindi quando si ordina da una colonna ENUM in modo specifico utilizzerà quell'indice interno anziché valori stringa (a meno che CAST () non venga utilizzato per tornare a un VARCHAR)
Simon at My School Portal

Non dovrebbe END DESC,essere END CASE DESC,?
Istiaque Ahmed,

No. Non tutti hanno CASEbisogno END CASE, dipende dal contesto. CASEall'interno di PROCEDURA richiede END CASE( dev.mysql.com/doc/refman/5.5/en/case.html ) tuttavia CASEall'interno di SELECT non richiede END CASE, semplicemente END( dev.mysql.com/doc/refman/5.7/en/… ) - in questo contesto è una funzione di flusso di controllo.
Simon sul portale My School,

1

Per il framework Yii2 possiamo raggiungere la seguente modalità

Project::find()
->orderBy([new Expression('FIELD(pid_is_t_m,2,0,1)'),'task_last_work'=> SORT_ASC])
->all();
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.