Cómo funciona ORDER BY FIELD () en MySQL internamente


37

Entiendo cómo ORDER BYfunciona la cláusula y cómo FIELD()funciona la función. Lo que quiero entender es cómo ambos trabajan juntos para clasificar. Cómo se recuperan las filas y cómo se deriva el orden de clasificación

+----+---------+
| 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)

La consulta anterior dará como resultado

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

algo similar a decir ORDER BY 3, 2, 1, 4

PREGUNTAS

  • ¿Cómo funciona esto internamente?
  • ¿Cómo obtiene MySQL las filas y calcula el orden de clasificación?
  • ¿Cómo sabe MySQL que tiene que ordenar por la columna id?

1
pruebe esta variación de su consulta: SELECT *, FIELD(id,3,2,1,4) AS f FROM mytable WHERE id IN (3,2,1,4);luego agregue ORDER BY fo ORDER BY FIELD(id,3,2,1,4)e intente nuevamente.
ypercubeᵀᴹ

Respuestas:


64

Para el registro

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

debería funcionar bien porque no tiene que ordenar la lista en la WHEREcláusula

En cuanto a cómo funciona,

  • CAMPO() es una función que devuelve la posición del índice de una lista delimitada por comas si el valor que está buscando existe.

  • Los ORDER BYvalores son evaluados por qué CAMPO () devuelve

Puedes crear todo tipo de pedidos elegantes

Por ejemplo, usando la función 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);

Esto hará que los primeros 4 identificadores aparezcan en la parte superior de la lista, de lo contrario, aparecerá en la parte inferior. ¿Por qué?

En el ORDER BY, obtienes 0 o 1.

  • Si la primera columna es 0, haga que aparezca cualquiera de los primeros 4 identificadores
  • Si la primera columna es 1, haga que aparezca después

Volteemos con DESC en la primera columna

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);

En el ORDER BY, todavía obtienes 0 o 1.

  • Si la primera columna es 1, haga que aparezcan los primeros 4 identificadores.
  • Si la primera columna es 0, haga que los primeros 4 identificadores aparezcan en el orden original

TU PREGUNTA REAL

Si realmente quieres cosas internas sobre esto, ve a las páginas 189 y 192 del Libro

MySQL Internals

para una verdadera inmersión profunda.

En esencia, hay una clase de C ++ llamada ORDER *order(El ORDER BYárbol de expresión). En JOIN::prepare, *orderse usa en una función llamada setup_order(). ¿Por qué en medio de la JOINclase? Cada consulta, incluso una consulta en una sola tabla siempre se procesa como JOIN (consulte mi publicación ¿Existe una diferencia de ejecución entre una condición JOIN y una condición WHERE? )

El código fuente de todo esto es sql/sql_select.cc

Evidentemente, el ORDER BYárbol va a mantener la evaluación de FIELD(id,3,2,1,4). Por lo tanto, los números 0,1,2,3,4 son los valores que se ordenan mientras llevan una referencia a la fila involucrada.


1
Esta es una explicación extraordinariamente excelente. Usando estos métodos pude obtener 3 órdenes, un primer valor primario que es el máximo del conjunto, luego por el CAMPO, luego por otra columna para los que no están en el conjunto del CAMPO. Algo que no hubiera soñado hace algún tiempo. Gracias por tomarse el tiempo para explicar realmente cómo funciona esto.
Lizardx

Supongamos que hay Nvalores en ambos INy FIELD. En este ejemplo N=4. ¿Entiendo correctamente que esta consulta va a realizar al menos ~N^2operaciones? Porque cada FIELDcálculo hace ~Ncomparaciones una vez para cada fila. Si es así, esto es bastante lento para grandes ¿ NTal vez no es un enfoque muy bueno?
Gherman

@Gherman La FIELD()función debería ser una O(1)operación porque FIELD()tiene un índice numérico id. Por lo tanto, no veo nada más pero O(n)basado en filas. No veo FIELD()hacer ninguna operación iterativa como la que GREATEST()tendría que hacer.
RolandoMySQLDBA

@RolandoMySQLDBA Mi punto es que si FIELDtiene Nargumentos para comparar, ejecutará Ncomparaciones. ¿De qué otra manera va a comparar un número con Notros números si no lo hace O(N)? La única posibilidad que se me ocurre es algún tipo de optimización a través de una estructura de datos especial como un hash o un árbol de argumentos. En realidad, sé que INsí tiene tal optimización. No lo se FIELD. ¿Qué quiere decir con un "índice numérico"?
Gherman

1
Hola @RaymondNijland, la declaración CASE es más comprensible. Para este caso, el azúcar sintáctico tiene menos escritura.
RolandoMySQLDBA

1

Tal vez esto esté demasiado lejos del código real, por lo que no tiene un nivel lo suficientemente bajo como lo que quería:

Cuando MySQL no puede usar el índice para recuperar datos en orden, crea una tabla / conjunto de resultados temporal con todas las columnas seleccionadas y algunos datos adicionales, uno de ellos es una especie de columna para almacenar los resultados del valor de expresión ORDER BY para cada fila. luego envía esta tabla tmp a una rutina "clasificador de archivos" con información por qué columna ordenar. Después de eso, las filas están ordenadas para que pueda elegirlas una por una y devolver las columnas seleccionadas.


Esta explicación no tiene en cuenta cómo FIELDfuncionan los cálculos. Me temo que puede tener un impacto significativo en el rendimiento.
Gherman

@Gherman No lo creo, a menos que esté usando una lista muy larga de argumentos (ya que la función es lineal al número de argumentos . El acceso a los datos es un orden de magnitud más lento que las simples comparaciones.
jkavalik

Sí, larga lista de argumentos. Hay tantos argumentos como registros hay en este ejemplo.
Gherman

Solo etiquetaría cientos o miles como muchos, y de todos modos
tendrá

¿Por qué no cientos de resultados? ¿Son muchos?
Gherman
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.