Come funziona internamente ORDER BY FIELD () in MySQL


37

Capisco come ORDER BYfunziona la clausola e come FIELD()funziona la funzione. Quello che voglio capire è come entrambi lavorano insieme per ordinare. Come vengono recuperate le righe e come viene derivato l'ordinamento

+----+---------+
| id |  name   |
+----+---------+
|  1 | stan    |
|  2 | kyle    |
|  3 | kenny   |
|  4 | cartman |
+----+---------+ 

SELECT * FROM mytable WHERE id IN (3,2,1,4) ORDER BY FIELD(id,3,2,1,4)

Verrà visualizzata la query sopra

+----+---------+
| id |  name   |
+----+---------+
|  3 | kenny   |
|  2 | kyle    |
|  1 | stan    |
|  4 | cartman |
+----+---------+ 

qualcosa di simile al dire ORDER BY 3, 2, 1, 4

DOMANDE

  • Come funziona internamente?
  • In che modo MySQL ottiene le righe e calcola l'ordinamento?
  • Come fa MySQL a sapere che deve ordinare per colonna id?

1
prova questa variante della tua query: SELECT *, FIELD(id,3,2,1,4) AS f FROM mytable WHERE id IN (3,2,1,4);quindi aggiungi ORDER BY fo ORDER BY FIELD(id,3,2,1,4)e riprova.
ypercubeᵀᴹ

Risposte:


64

Per il record

SELECT * FROM mytable WHERE id IN (1,2,3,4) ORDER BY FIELD(id,3,2,1,4);

dovrebbe funzionare anche perché non è necessario ordinare l'elenco nella WHEREclausola

Per quanto riguarda come funziona,

  • FIELD () è una funzione che restituisce la posizione dell'indice di un elenco delimitato da virgole se esiste il valore che stai cercando.

  • I ORDER BYvalori vengono valutati in base a ciò che restituisce FIELD ()

Puoi creare tutti i tipi di ordini fantasiosi

Ad esempio, utilizzando la funzione IF ()

SELECT * FROM mytable
WHERE id IN (1,2,3,4)
ORDER BY IF(FIELD(id,3,2,1,4)=0,1,0),FIELD(id,3,2,1,4);

Questo farà apparire i primi 4 ID in cima all'elenco, altrimenti apparirà in fondo. Perché?

In ORDER BY, ottieni 0 o 1.

  • Se la prima colonna è 0, fai apparire uno dei primi 4 ID
  • Se la prima colonna è 1, farla apparire in seguito

Capovolgilo con DESC nella prima colonna

SELECT * FROM mytable
WHERE id IN (1,2,3,4)
ORDER BY IF(FIELD(id,3,2,1,4)=0,1,0) DESC,FIELD(id,3,2,1,4);

In ORDER BY, ottieni ancora 0 o 1.

  • Se la prima colonna è 1, fai apparire solo i primi 4 ID.
  • Se la prima colonna è 0, fai apparire i primi 4 ID nell'ordine originale

LA TUA DOMANDA ATTUALE

Se vuoi davvero degli interni su questo, vai alle pagine 189 e 192 del libro

Interni MySQL

per un'immersione profonda.

In sostanza, esiste una classe C ++ chiamata ORDER *order(L' ORDER BYalbero delle espressioni). In JOIN::prepare, *orderviene utilizzato in una funzione chiamata setup_order(). Perché nel mezzo della JOINclasse? Ogni query, anche una query su una singola tabella, viene sempre elaborata come JOIN (Vedi il mio post Esiste una differenza di esecuzione tra una condizione JOIN e una condizione WHERE? )

Il codice sorgente per tutto questo è sql/sql_select.cc

Evidentemente, l' ORDER BYalbero terrà la valutazione di FIELD(id,3,2,1,4). Pertanto, i numeri 0,1,2,3,4 sono i valori ordinati mentre si porta un riferimento alla riga interessata.


1
Questa è una spiegazione straordinariamente eccellente. Usando questi metodi sono stato in grado di ottenere 3 ordini, un primo valore primario che è il massimo del set, quindi dal CAMPO, quindi da un'altra colonna per quelli non nel set FIELD. Qualcosa che non avrei mai sognato qualche tempo fa. Grazie per aver dedicato del tempo a spiegare davvero come funziona davvero.
Lizardx,

Supponiamo che ci siano Nvalori in entrambi INe FIELD. In questo esempio N=4. Comprendo correttamente che questa query eseguirà almeno ~N^2operazioni. Perché ogni FIELDcalcolo fa ~Nconfronti una volta per ogni riga. Se è così, questo è abbastanza lento per i grandi, Nforse non è un ottimo approccio?
Gherman,

@Gherman La FIELD()funzione dovrebbe essere O(1)un'operazione perché FIELD()ha un indice numerico id. Quindi non vedo nient'altro che O(n)basato sulle righe. Non vedo FIELD()fare alcuna operazione iterativa come GREATEST()dovrebbe fare.
RolandoMySQLDBA

@RolandoMySQLDBA Il mio punto è che se FIELDha Nargomenti da confrontare, eseguirà i Nconfronti. In quale altro modo si confronterà un numero con Naltri numeri se non facendo O(N)? L'unica possibilità che mi viene in mente è una sorta di ottimizzazione attraverso una struttura dati speciale come un hash o un albero di argomenti. In realtà so che INha una tale ottimizzazione. Non lo so FIELD. Cosa intendi per "indice numerico"?
Gherman,

1
Ehi @RaymondNijland, L'affermazione CASE è più comprensibile, in questo caso lo zucchero sintattico ha solo meno scrittura.
RolandoMySQLDBA,

1

Forse questo sarà troppo lontano dal codice reale, quindi non abbastanza basso da quello che volevi:

Quando MySQL non può utilizzare l'indice per recuperare i dati in ordine, crea una tabella / gruppo di risultati temporanei con tutte le colonne selezionate e alcuni dati aggiuntivi - uno di questi è una specie di colonna per memorizzare i risultati del valore dell'espressione ORDER BY per ogni riga - quindi invia questa tabella tmp a una routine "filesort" con informazioni su quale colonna ordinare. Dopodiché le righe sono in ordine in modo che possano selezionarle una per una e restituire le colonne selezionate.


Questa spiegazione non tiene conto di come viene FIELDcalcolata la funzione. Temo che possa avere un impatto significativo sulle prestazioni.
Gherman,

@Gherman Non credo, a meno che tu non stia usando un elenco molto lungo di argomenti (poiché la funzione è lineare al numero di argomenti . L'accesso ai dati è un ordine di grandezza più lento dei semplici confronti.
jkavalik

Sì, lungo elenco di argomenti. Ci sono tanti argomenti quanti sono i record in questo esempio.
Gherman,

Vorrei solo etichettarne centinaia o migliaia, e poi avresti comunque altri problemi (dimensione della query ecc.)
jkavalik,

perché non centinaia di risultati? Sono molti?
Gherman,
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.