sql >> Base de Datos >  >> RDS >> Mysql

¿El orden de los campos en una cláusula WHERE afecta el rendimiento en MySQL?

SQL fue diseñado para ser un lenguaje declarativo, no de procedimiento. Por lo tanto, el optimizador de consultas no debería considere el orden de los predicados de la cláusula where para determinar cómo aplicarlos.

Probablemente voy a simplificar demasiado la siguiente discusión sobre un optimizador de consultas SQL. Escribí uno hace años, en este sentido (¡fue muy divertido!). Si realmente quiere profundizar en la optimización de consultas moderna, consulte SQL Tuning , de O'Reilly.

En un optimizador de consultas SQL simple, la instrucción SQL primero se compila en un árbol de álgebra relacional operaciones. Cada una de estas operaciones toma una o más tablas como entrada y produce otra tabla como salida. Escanear es un escaneo secuencial que lee una tabla desde la base de datos. Ordenar produce una tabla ordenada. Seleccionar produce una tabla cuyas filas se seleccionan de otra tabla de acuerdo con alguna condición de selección. Proyecto produce una tabla con solo ciertas columnas de otra tabla. Producto cruzado toma dos tablas y produce una tabla de salida compuesta por todos los pares imaginables de sus filas.

De manera confusa, la cláusula SQL SELECT se compila en un Proyecto de álgebra relacional , mientras que la cláusula WHERE se convierte en un álgebra relacional Select . La cláusula FROM se convierte en uno o más Joins , cada uno tomando dos mesas y produciendo una mesa fuera. Hay otras operaciones de álgebra relacional que involucran unión de conjuntos, intersección, diferencia y pertenencia, pero mantengamos esto simple.

Este árbol realmente necesita ser optimizado. Por ejemplo, si tiene:

select E.name, D.name 
from Employee E, Department D 
where E.id = 123456 and E.dept_id = D.dept_id

con 5000 empleados en 500 departamentos, la ejecución de un árbol no optimizado producirá ciegamente todas las combinaciones posibles de un empleado y un departamento (un producto cruzado ) y luego Seleccionar solo la combinación que se necesitaba. El Escaneo of Employee producirá una tabla de 5000 registros, el Escanear del departamento producirá una tabla de 500 registros, el producto cruzado de esas dos tablas producirá una tabla de 2 500 000 registros, y Select en E.id tomará esa tabla de 2 500 000 registros y descartará todos menos uno, el registro que se buscaba.

[Los procesadores de consultas reales intentarán no materializar todas estas tablas intermedias en la memoria, por supuesto.]

Entonces, el optimizador de consultas recorre el árbol y aplica varias optimizaciones. Una es dividir cada Select en una cadena de Selecciones , uno para cada uno de los Select originales Las condiciones de nivel superior de , las que y-ed juntas. (Esto se llama "forma normal conjuntiva".) Luego, el individuo más pequeño Selecciona se mueven en el árbol y se fusionan con otras operaciones de álgebra relacional para formar otras más eficientes.

En el ejemplo anterior, el optimizador primero empuja el botón Select en E.id =123456 debajo del costoso Producto cruzado operación. Esto significa el Producto Cruz solo produce 500 filas (una para cada combinación de ese empleado y un departamento). Luego, el nivel superior Seleccionar for E.dept_id =D.dept_id filtra las 499 filas no deseadas. No está mal.

Si hay un índice en el campo de identificación del empleado, entonces el optimizador puede combinar el Escanear de empleado con Seleccionar en E.id =123456 para formar un índice rápido Buscar . Esto significa que solo se lee una fila de empleados en la memoria desde el disco en lugar de 5000. Las cosas están mejorando.

La optimización principal final es tomar el Seleccionar en E.dept_id =D.dept_id y combínelo con el Producto cruzado . Esto lo convierte en un álgebra relacional Equijoin operación. Esto no hace mucho por sí mismo. Pero si hay un índice en Department.dept_id, entonces el Escaneo secuencial de nivel inferior del Departamento alimentando el Equijoin se puede convertir en un índice muy rápido Buscar del registro del Departamento de nuestro empleado.

Las optimizaciones menores implican empujar Proyecto operaciones hacia abajo. Si el nivel superior de su consulta solo necesita E.name y D.name, y las condiciones necesitan E.id, E.dept_id y D.dept_id, entonces Escanear las operaciones no tienen que construir tablas intermedias con todas las demás columnas, ahorrando espacio durante la ejecución de la consulta. Hemos convertido una consulta horriblemente lenta en dos búsquedas de índice y nada más.

Acercándonos más a la pregunta original, digamos que tienes:

select E.name 
from Employee E 
where E.age > 21 and E.state = 'Delaware'

El árbol de álgebra relacional no optimizado, cuando se ejecuta, escanearía a los 5000 empleados y produciría, digamos, los 126 en Delaware que tienen más de 21 años. El optimizador de consultas también tiene una idea aproximada de los valores en la base de datos. Puede saber que la columna E.state tiene los 14 estados en los que la empresa tiene ubicaciones y algo sobre las distribuciones de E.age. Así que primero ve si alguno de los campos está indexado. Si E.state lo es, tiene sentido usar ese índice para seleccionar solo la pequeña cantidad de empleados que el procesador de consultas sospecha que están en Delaware según sus últimas estadísticas calculadas. Si solo lo es E.age, es probable que el procesador de consultas decida que no vale la pena, ya que el 96 % de todos los empleados tienen 22 años o más. Entonces, si E.state está indexado, nuestro procesador de consultas rompe el Seleccionar y fusiona E.state ='Delaware' con Scan para convertirlo en un Escaneo de índice mucho más eficiente .

Digamos en este ejemplo que no hay índices en E.state y E.age. El Seleccionar combinado La operación tiene lugar después del "Escaneo" secuencial de Empleado. ¿Hace alguna diferencia qué condición en Seleccionar se hace primero? Probablemente no mucho. El procesador de consultas podría dejarlos en el orden original en la instrucción SQL, o podría ser un poco más sofisticado y considerar el gasto esperado. A partir de las estadísticas, nuevamente encontraría que la condición E.state ='Delaware' debería ser más selectiva, por lo que invertiría las condiciones y haría eso primero, de modo que solo haya 126 E.age> 21 comparaciones en lugar de 5,000 . O podría darse cuenta de que las comparaciones de igualdad de cadenas son mucho más costosas que las comparaciones de enteros y dejar el orden en paz.

En cualquier caso, todo esto es muy complejo y es muy poco probable que su orden de condición sintáctica marque una diferencia. No me preocuparía a menos que tenga un problema de rendimiento real y el proveedor de su base de datos use el orden de condición como una pista.