Este artículo lo lleva a comprender la optimización basada en reglas de MySQL

Prefacio:

No olvide que MySQL es esencialmente un software. MySQL no requiere que todos los que usan este software sean maestros de la base de datos, al igual que cuando escribí este artículo, no puedo exigir que todos conozcan el conocimiento antes de aprender. En otras palabras, no podemos evitar que algunos socios pequeños escriban algunas declaraciones cuya ejecución consume mucho rendimiento. Aun así, MySQL todavía se basa en hacer 一些规则todo lo posible para convertir esta declaración tan mala en una forma que se pueda ejecutar de manera más eficiente 查询重写(escríbalo de nuevo), este capítulo es principalmente para explicar en detalle estas reglas de reescritura más importantes.

1. Condiciones simplificadas

La condición de búsqueda de la declaración de consulta que escribimos es esencialmente una expresión. Estas expresiones pueden ser complicadas o no pueden ejecutarse de manera eficiente. El optimizador de consultas de MySQL simplificará estas expresiones para nosotros. Para facilitar la comprensión de todos, usaremos letras simples como a, y similares para representar los nombres de las columnas de una determinada tabla en los siguientes ejemplos .bc

1.1 Eliminar paréntesis innecesarios

A veces hay muchos paréntesis inútiles en la expresión, como este:

((a = 5 AND b =c ) OR ((a > c) AND (c < 5)))

Es molesto de ver, el optimizador eliminará todos los paréntesis que no se usan, eso es todo:

(a = 5 AND b=c) OR (a > c AND c < 5)

1.2 Transferencia constante (constant_propagation)

A veces, una expresión es una coincidencia equivalente entre una determinada columna y una determinada constante, como esta:

a = 5

Cuando esta expresión se conecta con otras aexpresiones que involucran columnas AND, el valor de a en otras expresiones se puede reemplazar por 5, por ejemplo:

a = 5 AND b > a

se puede convertir a:

a = 5 AND b > 5

小提示:
¿Por qué no se pueden usar las expresiones conectadas por OR para la transferencia constante? Piénsalo tú mismo~

1.3 Transferencia equivalente (equality_propagation)

A veces hay una relación de coincidencia de equivalencia entre varias columnas, como esta:

a = b AND b = c AND c = 5

Esta expresión se puede simplificar a:

a = 5 AND b = 5 AND c = 5

1.4 Eliminar condiciones inútiles (trivial_condition_removal)

Para algunas expresiones que obviamente son siempre TRUEo FALSE, el optimizador las eliminará, como esta expresión:

(a < 1 AND b = b) OR (a = 6 OR 5 != 5) 

Obviamente, b = besta expresión es siempre TRUE, 5 != 5esta expresión es siempre FALSEy todas las expresiones simplificadas son así:

(a < 1 AND TRUE) OR (a = 6 OR 5 FALSE) 

Se puede simplificar aún más:

a < 1  OR a = 6

1.5 Cálculo de expresiones

Antes de que comience la consulta, si la expresión contiene solo constantes, su valor se calculará primero, como este:

a = 5 + 1

Dado que 5 + 1esta expresión contiene solo constantes, se simplifica a:

a = 6

Pero lo que debe tenerse en cuenta aquí es que si una columna no se usa como operando de una expresión en una forma separada, como aparecer en una función o en una expresión más compleja, como esta:

ABS(a) > 5

o:

-a = -8

优化器是不会尝试对这些表达式进行简化的. Dijimos antes que solo cuando ciertos operadores usados ​​en la columna de índice y constantes en la condición de búsqueda están conectados, se puede usar el índice.Si el índice está disponible, 最好让索引列以单独的形式出现在表达式中.

1.6 Combinación de la cláusula HAVING y la cláusula WHERE

SUMSi las funciones y cláusulas agregadas como , MAXetc. no aparecen en la consulta GROUP BY, el optimizador combina HAVINGlas cláusulas y las cláusulas.WHERE

1.7 Detección de tabla constante

MySQL cree que las siguientes dos consultas se ejecutan muy rápido

  • No hay o solo hay un registro en la tabla de consulta

小提示:
¿Crees que hay algo mal con este?¿Cómo puedo saber cuántos registros hay en la tabla antes de comenzar a revisar la tabla? Jaja~ Esto en realidad se basa en estadísticas. Sin embargo, dijimos que las estadísticas de InnoDB son inexactas, por lo que este artículo no se puede usar para tablas que usan InnoDB como motor de almacenamiento, sino solo para tablas que usan motores de almacenamiento Memory o MyISAM.

  • Use la coincidencia de equivalencia de clave principal o la coincidencia de equivalencia de columna de índice secundario único como condición de búsqueda para consultar una tabla

MySQL siente que el tiempo empleado por estas dos consultas es tan pequeño que puede ignorarse, por lo que la tabla consultada por estos dos métodos se llama 常量表(nombre en inglés: constant tables). Al analizar y consultar una declaración, el optimizador primero ejecuta la consulta de la tabla constante, luego reemplaza todas las condiciones relacionadas con la tabla en la consulta con constantes y finalmente analiza el costo de consulta de otras tablas, por ejemplo, encuentra una declaración de consulta :

SELECT * FROM table1 INNER JOIN table2 
    ON table1.column1 = table2.column2 
    WHERE table1.primary_key = 1;

Obviamente, esta consulta puede usar la coincidencia equivalente de la clave principal y el valor constante para consultar la table1tabla, es decir, es equivalente a la tabla constante en esta consulta , y la consulta de la tabla se ejecutará antes que el costo de consulta de table1se analiza table2la tabla table1, y se reemplaza la consulta Las condiciones que involucran table1a la tabla, es decir, la sentencia anterior se transforma en esta:

SELECT table1表记录的各个字段的常量值,table2.* 
    FROM table1 INNER JOIN table2 
    ON table1.column1列的常量值 = table2.column2 ;

2. Eliminación de la conexión exterior

Como dijimos antes, las posiciones de la mesa conductora y la mesa conducida de la conexión interna se pueden convertir entre sí, mientras que la mesa 左外连接conductora 右外连接y la mesa conducida de la suma son fijas. Esto permite 内连接reducir el costo total de la consulta al optimizar el orden de combinación de las tablas, pero 外连接el orden de combinación de las tablas no se puede optimizar. demo9Para el buen progreso de nuestro estudio, invitamos a la tabla de suma que usamos cuando presentamos el principio de conexión antes demo10Para evitar que todos se olviden, echemos un vistazo a la estructura de estas dos tablas:

mysql> create table demo9 (m1 int, n1 char(1));
Query OK, 0 rows affected (0.01 sec)

mysql> create table demo10 (m2 int, n2 char(1));
Query OK, 0 rows affected (0.03 sec)

Para despertar la memoria de todos, mostremos los datos en estas dos tablas:

mysql> select * from demo9;
+------+------+
| m1   | n1   |
+------+------+
|    1 | a    |
|    2 | b    |
|    3 | c    |
+------+------+
3 rows in set (0.00 sec)

mysql> select * from demo10;
+------+------+
| m2   | n2   |
+------+------+
|    2 | b    |
|    3 | c    |
|    4 | d    |
+------+------+
3 rows in set (0.00 sec)

Como dijimos antes, 外连接和内连接的本质就是:对于外连接驱动表的记录来说,如果无法在被驱动表中找到匹配ON子句中的过滤条件的记录,那么该记录仍然会被加入到结果集中,对应的被驱动表各个字段使用NULL值填充;而内连接的驱动表的记录如果无法在被驱动表中找到匹配ON子句中的过滤条件的记录,那么该记录会被舍弃. El efecto de consulta es así:

mysql> SELECT * FROM demo9 INNER JOIN demo10 ON demo9.m1 = demo10.m2;
+------+------+------+------+
| m1   | n1   | m2   | n2   |
+------+------+------+------+
|    2 | b    |    2 | b    |
|    3 | c    |    3 | c    |
+------+------+------+------+
2 rows in set (0.00 sec)

mysql> SELECT * FROM demo9 LEFT  JOIN demo10 ON demo9.m1 = demo10.m2;
+------+------+------+------+
| m1   | n1   | m2   | n2   |
+------+------+------+------+
|    1 | a    | NULL | NULL |
|    2 | b    |    2 | b    |
|    3 | c    |    3 | c    |
+------+------+------+------+
3 rows in set (0.00 sec)

Para la combinación externa izquierda en el ejemplo anterior, dado que el registro directamentedemo9en de la tabla y la columna se establecen en .m1=1n1='a'demo10m2n2NULL

小提示:
La combinación externa izquierda y la combinación externa derecha en realidad solo son diferentes en el método de selección de la tabla de control, y el resto son iguales, por lo que el optimizador primero convertirá la consulta de combinación externa derecha en una consulta de combinación externa izquierda. Dejaremos de quejarnos sobre la conexión externa correcta más adelante.

Sabemos WHEREque las cláusulas son más letales, 凡是不符合WHERE子句中条件的记录都不会参与连接. Siempre que especifiquemos en la condición de búsqueda que el valor de la columna relevante de la tabla controlada no sea NULL, entonces en la combinación externa, ningún registro de la tabla controladora que cumpla con la condición de la cláusula ON en la tabla controlada será excluido de el conjunto de resultados final. , es decir: 在这种情况下:外连接和内连接也就没有什么区别了! Por ejemplo esta consulta:

mysql> SELECT * FROM demo9 LEFT JOIN demo10 on demo9.m1 = demo10.m2 WHERE demo10.n2 IS NOT NULL;
+------+------+------+------+
| m1   | n1   | m2   | n2   |
+------+------+------+------+
|    2 | b    |    2 | b    |
|    3 | c    |    3 | c    |
+------+------+------+------+
2 rows in set (0.00 sec)

Dado que la columna especificada demo10de la tabla controlada n2no está permitida , la consulta de combinación externa izquierda y la consulta de combinación interna de la tabla NULLanterior son las mismas. Por supuesto, no necesitamos especificar explícitamente una columna de la tabla impulsada , siempre que tenga este significado implícito, por ejemplo:demo9demo10IS NOT NULL

mysql> SELECT * FROM demo9 LEFT JOIN demo10 on demo9.m1 = demo10.m2 WHERE demo10.m2 = 2;
+------+------+------+------+
| m1   | n1   | m2   | n2   |
+------+------+------+------+
|    2 | b    |    2 | b    |
+------+------+------+------+
1 row in set (0.01 sec)

En este ejemplo, especificamos WHEREque la columna demo10de la tabla impulsada es igual a en la cláusula , lo que equivale a especificar indirectamente que la columna no es un valor, por lo que la consulta de combinación externa izquierda anterior es en realidad equivalente a la consulta de combinación interna a continuación. de:m22m2NULL

mysql> SELECT * FROM demo9 INNER JOIN demo10 on demo9.m1 = demo10.m2 WHERE demo10.m2 = 2;
+------+------+------+------+
| m1   | n1   | m2   | n2   |
+------+------+------+------+
|    2 | b    |    2 | b    |
+------+------+------+------+
1 row in set (0.01 sec)

Llamamos a esta condición en la consulta de combinación externa que la WHEREcláusula especificada contiene una columna en la tabla controlada NULLque no es un valor 空值拒绝(nombre en inglés: reject-NULL). 在被驱动表的WHERE子句符合空值拒绝的条件后,外连接和内连接可以相互转换. El beneficio de esta conversión es que 查询优化器可以通过评估表的不同连接顺序的成本,选出成本最低的那种连接顺序来执行查询.

3. Optimización de subconsultas

Nuestro tema fue originalmente sobre cómo el optimizador de consultas de MySQL maneja las subconsultas, pero todavía me preocupa que muchos estudiantes no dominen la sintaxis de las consultas de unión, por lo que primero aprenderemos qué es una subconsulta (por supuesto, no cubriremos todo, solo vamos a hable sobre eso ~), y luego aprenda sobre la optimización de subconsultas.

3.1 Sintaxis de subconsulta

Presumiblemente, todos nacieron de sus madres, incluso los nietos tienen madres—— 石头人. Lo que hay en el vientre de una madre embarazada es su hijo. Del mismo modo, puede haber otra declaración de consulta en una determinada posición en una declaración de consulta. Esta consulta que aparece en una determinada posición de una declaración de consulta se llama For (también podemos 子查询llamar it a baby query ha~), la consulta que juega el papel de "madre" también se llama 外层查询. A diferencia de cuando estamos embarazadas y los bebés solo están en la barriga, en una consulta externa pueden aparecer subconsultas en varios lugares, como por ejemplo:

1. En la cláusula SELECT

Así es como solemos llamar a la lista de consultas, como esta:

mysql> SELECT (SELECT m1 FROM demo9 LIMIT 1);
+--------------------------------+
| (SELECT m1 FROM demo9 LIMIT 1) |
+--------------------------------+
|                              1 |
+--------------------------------+
1 row in set (0.01 sec)

Entre ellos (SELECT m1 FROM demo9 LIMIT 1)está nuestra subconsulta persistente.

2. En la cláusula FROM

Por ejemplo:

mysql> SELECT m,n FROM (SELECT m2+1 AS m, n2 AS n FROM demo10 WHERE m2 > 1) AS t;
+------+------+
| m    | n    |
+------+------+
|    3 | b    |
|    4 | c    |
|    5 | d    |
+------+------+
3 rows in set (0.00 sec)

La subconsulta en este ejemplo es: (SELECT m2 + 1 AS m, n2 AS n FROM demo10 WHERE m2 > 1), que es especial porque aparece en FROMla cláusula. FROM¿No está el nombre de la tabla que queremos consultar almacenado aquí en la cláusula?¿Qué diablos es una subconsulta aquí? De hecho, aquí podemos considerar el resultado de la consulta de la subconsulta como una tabla. La subconsulta detrás de la subconsulta AS tindica que el resultado de la subconsulta es equivalente a una tabla denominada t, y las columnas de la tabla denominada t son los resultados de la subconsulta. Columna, por ejemplo, la tabla del ejemplo ttiene dos columnas: mcolumna y ncolumna. Esta FROMsubconsulta colocada en la cláusula es esencialmente equivalente a una tabla, pero es un poco diferente de la tabla que usamos habitualmente. MySQL llama a esta tabla compuesta de conjuntos de resultados de subconsultas 派生表.

3. En la cláusula WHERE o ON

WHEREPoner subconsultas en las cláusulas o cláusulas de la consulta externa ONpuede ser la forma más común en que usamos subconsultas, como esta:

mysql> SELECT * FROM demo9 WHERE m1 IN (SELECT m2 FROM demo10);
+------+------+
| m1   | n1   |
+------+------+
|    2 | b    |
|    3 | c    |
+------+------+
2 rows in set (0.00 sec)

Esta consulta indica que queremos usar (SELECT m2 FROM demo10)el resultado de esta subconsulta como el parámetro de declaración IN de la consulta externa.La declaración de consulta completa significa que queremos encontrar demo9algunos registros en la tabla cuyos m1valores de columna puedan encontrar una coincidencia en demo10la tabla columnas m2valor

4. En las cláusulas ORDER BY y GROUP BY

Aunque la gramática lo admite, no tiene ningún sentido, así que no te preocupes más por esta situación.

3.1.1 Diferenciar subconsultas por conjuntos de resultados devueltos

Debido a que una subconsulta es en sí misma una consulta, estas subconsultas se pueden dividir en diferentes tipos según los diferentes tipos de conjuntos de resultados que devuelven:

1. Subconsultas escalares

Aquellas subconsultas que devuelven un único valor se denominan 标量子查询, por ejemplo:

mysql> SELECT (SELECT m1 FROM demo9 LIMIT 1);
+--------------------------------+
| (SELECT m1 FROM demo9 LIMIT 1) |
+--------------------------------+
|                              1 |
+--------------------------------+
1 row in set (0.00 sec)

o esto:

mysql> SELECT * FROM demo9 WHERE m1 = (SELECT MIN(m2) FROM demo10);
+------+------+
| m1   | n1   |
+------+------+
|    2 | b    |
+------+------+
1 row in set (0.00 sec)

Las subconsultas en ambas consultas devuelven un solo valor, que es un escalar. Estas subconsultas escalares pueden aparecer en varios lugares de la declaración de consulta como un valor único o como parte de una expresión.

2. Subconsultas de fila

Como sugiere el nombre, es para devolver 一条记录的子查询, pero este registro debe contener varias columnas (solo una columna se convierte en una subconsulta escalar). Por ejemplo:

mysql> SELECT * FROM demo9 WHERE (m1, n1) = (SELECT m2, n2 FROM demo10 LIMIT 1);
+------+------+
| m1   | n1   |
+------+------+
|    2 | b    |
+------+------+
1 row in set (0.00 sec)

Entre ellos (SELECT m2, n2 FROM demo10 LIMIT 1)se encuentra una subconsulta de fila. El significado de la declaración completa es demo9encontrar algunos registros de la tabla. La suma m1y las columnas de estos registros son respectivamente iguales a la suma y las columnas n1en el resultado de la subconsulta .m2n2

3. Consulta de columna

Las subconsultas de columna son naturales 查询出一个列的数据, pero los datos de esta columna deben contener varios registros (solo un registro se convierte en una subconsulta escalar). Por ejemplo:

mysql> SELECT * FROM demo9 WHERE m1 IN (SELECT m2 FROM demo10);
+------+------+
| m1   | n1   |
+------+------+
|    2 | b    |
|    3 | c    |
+------+------+
2 rows in set (0.00 sec)

Entre ellos (SELECT m2 FROM demo10)se encuentra una subconsulta de columna, que indica que el valor de la columna demo10en la tabla de consulta se usa como parámetro de la declaración de consulta externa.m2IN

4. Subconsulta de tabla

Como su nombre lo indica, es el resultado de una subconsulta 既包含很多条记录,又包含很多个列, como esta:

mysql> SELECT * FROM demo9 WHERE (m1, n1) IN (SELECT m2, n2 FROM demo10);
+------+------+
| m1   | n1   |
+------+------+
|    2 | b    |
|    3 | c    |
+------+------+
2 rows in set (0.00 sec)

Una de ellas (SELECT m2, n2 FROM demo10)es una subconsulta de tabla. Aquí debemos compararla con la subconsulta de fila 行子查询中我们用了LIMIT 1para asegurarnos de que el resultado de la subconsulta sea solo un registro. Esta restricción no es necesaria en la subconsulta de tabla.

3.1.2 Distinguir las subconsultas según su relación con la consulta externa

1. Subconsultas no correlacionadas

Si la subconsulta puede ejecutarse sola para producir resultados, entonces 不依赖于外层查询的值podemos llamar a esta subconsulta una subconsulta 不相关子查询. Todas las subconsultas que presentamos anteriormente pueden considerarse como subconsultas irrelevantes, por lo que no daré ejemplos~

2. Subconsultas correlacionadas

Si la ejecución de la subconsulta lo requiere 依赖于外层查询的值, podemos llamar a esta subconsulta 相关子查询. Por ejemplo:

mysql> SELECT * FROM demo9 WHERE m1 IN (SELECT m2 FROM demo10 WHERE n1 = n2);
+------+------+
| m1   | n1   |
+------+------+
|    2 | b    |
|    3 | c    |
+------+------+
2 rows in set (0.00 sec)

La subconsulta del ejemplo es (SELECT m2 FROM demo10 WHERE n1 = n2), pero hay una condición de búsqueda en esta consulta n1 = n2, no olvides que n1es la columna de la tabla demo9, es decir la columna de la consulta externa, es decir la ejecución de la subconsulta es obligatorio 依赖于外层查询的值, por lo que esta subconsulta es uno 相关子查询.

3.1.3 Uso de subconsultas en expresiones booleanas

¿Qué quieres decir con escribir una subconsulta como esta:

SELECT (SELECT m1 FROM demo9 LIMIT 1);

Parece sin sentido ~ Usualmente usamos la subconsulta más como 布尔表达式parte de ella como condición de búsqueda y la usamos en WHEREcláusulas o ONcláusulas. Entonces, resumamos los escenarios de uso de subconsultas en expresiones booleanas aquí.

1. Usar =、>、<、>=、<=、<>、!=、<=>operadores como expresiones booleanas

No necesito presentar el significado específico de estos operadores. Si no lo sabe, realmente admiro su coraje al ver esto aquí ~ Por conveniencia, llamamos a estos operadores Bueno, por lo que la expresión booleana compuesta de subconsultas se parece comparison_operatora este:

操作数 comparison_operator (子查询)

Aquí 操作数puede haber un nombre de columna, una constante, una expresión más compleja o incluso otra subconsulta. Pero cabe señalar que 这里的子查询只能是标量子查询或者行子查询,也就是子查询的结果只能返回一个单一的值或者只能是一条记录.

Así (subconsulta escalar):

mysql> SELECT * FROM demo9 WHERE m1 < (SELECT MIN(m2) FROM demo10);
+------+------+
| m1   | n1   |
+------+------+
|    1 | a    |
+------+------+
1 row in set (0.00 sec)

o esto (subconsulta de fila):

mysql> SELECT * FROM demo9 WHERE (m1, n1) = (SELECT m2, n2 FROM demo10 LIMIT 1);
+------+------+
| m1   | n1   |
+------+------+
|    2 | b    |
+------+------+
1 row in set (0.00 sec)

2. [NO] EN/CUALQUIERA/ALGUNAS/TODAS las subconsultas

Para subconsultas de columna y subconsultas de tabla, sus conjuntos de resultados contienen muchos registros. Estos registros son equivalentes a un conjunto, por lo que no se pueden usar simplemente con otro operando para formar una expresión booleana. MySQL usa la siguiente sintaxis para formar un comparison_operatorbooleano expresión con un operando y un conjunto:

a. EN 或 者 NO EN

La forma gramatical específica es la siguiente:操作数 [NOT] IN (子查询)

Esta expresión booleana se utiliza para determinar si un operando está en el conjunto de conjuntos de resultados de la subconsulta. Por ejemplo, la siguiente consulta significa encontrar algunos registros en la tabla demo9, que existen en los resultados de la subconsulta concentrada:

mysql> SELECT * FROM demo9 WHERE (m1, n1) IN (SELECT m2, n2 FROM demo10);
+------+------+
| m1   | n1   |
+------+------+
|    2 | b    |
|    3 | c    |
+------+------+
2 rows in set (0.00 sec)


b. La forma gramatical específica de ANY/SOME (ANY y SOME son sinónimos) es la siguiente:操作数 comparison_operator ANY/SOME(子查询)

Esta expresión booleana significa que siempre que haya un valor en el conjunto de resultados de la subconsulta y se compare el operando dado comparison_operator, TRUEel resultado de toda la expresión es TRUE, de lo contrario, el resultado de toda la expresión es FALSE. Por ejemplo, la siguiente consulta:

mysql> SELECT * FROM demo9 WHERE m1 > ANY(SELECT m2 FROM demo10);
+------+------+
| m1   | n1   |
+------+------+
|    3 | c    |
+------+------+
1 row in set (0.00 sec)

El significado de esta consulta es que para el valor de la columna demo9de un determinado registro en la tabla , si hay un valor más pequeño que la columna en el conjunto de resultados de la subconsulta , entonces el valor de toda la expresión booleana es , de lo contrario , es decir, siempre que el valor de la columna sea mayor que el valor más pequeño en el conjunto de resultados de la subconsulta, el resultado de la expresión completa es , por lo que la consulta anterior es esencialmente equivalente a esta consulta:m1(SELECT m2 FROM demo10)m1TRUEFALSEm1TRUE

mysql> SELECT * FROM demo9 WHERE m1 > (SELECT MIN(m2) FROM demo10);
+------+------+
| m1   | n1   |
+------+------+
|    3 | c    |
+------+------+
1 row in set (0.01 sec)

además,=ANY相当于判断子查询结果集中是否存在某个值和给定的操作数相等,它的含义和IN是相同的

C. TODO

La forma gramatical específica es la siguiente:操作数 comparison_operator ALL(子查询)

comparison_operatorEsta expresión booleana significa que el resultado de comparar todos los valores en el conjunto de resultados de la subconsulta con el operando dado es TRUE, luego el resultado de toda la expresión es TRUE, de lo contrario, el resultado de toda la expresión es FALSE. Por ejemplo, la siguiente consulta:

mysql> SELECT * FROM demo9 WHERE m1 > ALL(SELECT m2 FROM demo10);
Empty set (0.00 sec)

El significado de esta consulta es que para el valor de demo9una columna de un registro en la tabla , si todos los valores en el conjunto de resultados de la subconsulta son mayores que el valor de la columna, entonces el valor de la expresión booleana completa es , de lo contrario , es decir, siempre que m1 El valor de la columna sea mayor que el valor más pequeño en el conjunto de resultados de la subconsulta, el resultado de la expresión completa es , por lo que la consulta anterior es esencialmente equivalente a esta consulta:m1(SELECT m2 FROM demo10)m1TRUEFALSETRUE

mysql> SELECT * FROM demo9 WHERE m1 > (SELECT MAX(m2) FROM demo10);
Empty set (0.00 sec)

subconsulta D. EXISTE

A veces solo necesitamos juzgar si hay registros en el conjunto de resultados de la subconsulta, y no importa cuál sea el registro. Puede usar put EXISTSo NOT EXISTSdelante de la declaración de la subconsulta, así:[NOT] EXISTS (子查询)

Pongamos un ejemplo:

mysql> SELECT * FROM demo9 WHERE EXISTS (SELECT 1 FROM demo10);
+------+------+
| m1   | n1   |
+------+------+
|    1 | a    |
|    2 | b    |
|    3 | c    |
+------+------+
3 rows in set (0.00 sec)

Para la subconsulta (SELECT 1 FROM demo10), no nos importa cuál sea el resultado final de la subconsulta, por lo que no importa si completa *, un nombre de columna o cualquier otra cosa en la lista de consultas. Lo que realmente nos importa es la subconsulta Si el registro existe en el conjunto de resultados. Es decir, siempre que (SELECT 1 FROM demo10)haya registros en esta consulta, el resultado de toda la expresión EXISTS es TRUE.

3.1.4 Consideraciones sobre la sintaxis de las subconsultas

1. Las subconsultas deben expandirse con paréntesis

Las subconsultas no expandidas son ilegales, como esta:

mysql> SELECT SELECT m1 FROM demo9;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your
MySQL server version for the right syntax to use near 'SELECT m1 FROM demo9' at line 1

2. La subconsulta en la cláusula SELECT debe ser una subconsulta escalar

Si hay alguno en el conjunto de resultados de la subconsulta 多个列或者多个行, no se permite colocarlos SELECTen la cláusula, es decir, en la lista de consultas. Por ejemplo, esto es ilegal:

mysql> SELECT (SELECT m1, n1 FROM demo9);
ERROR 1241 (21000): Operand should contain 1 column(s)

3. Cuando desee obtener una subconsulta escalar o una subconsulta de fila, pero no puede garantizar que el conjunto de resultados de la subconsulta tenga solo un registro, debe usar la instrucción LIMIT 1 para limitar la cantidad de registros.

4. Para la subconsulta [NOT] IN/ANY/SOME/ALL, la instrucción LIMIT no está permitida en la subconsulta

Por ejemplo, esto es ilegal:

mysql> SELECT * FROM demo9 WHERE m1 IN (SELECT * FROM demo10 LIMIT 2);
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'

¿Por qué es ilegal? Solo lo estipulan otros, así que no lo explico ~ Tal vez las versiones futuras lo admitan. Solo porque [NOT] IN/ANY/SOME/ALLla subconsulta no admite la declaración LIMIT, estas declaraciones en la subconsulta son redundantes

5. Cláusula secundaria ORDER BY

El resultado de una subconsulta es en realidad equivalente a un conjunto, y no importa si los valores del conjunto están ordenados o no. Por ejemplo, las ORDER BYcláusulas en la siguiente declaración son simplemente superfluas:

SELECT * FROM demo9 WHERE m1 IN (SELECT m2 FROM demo10 ORDER BY m2);

6. Declaración DISTINTA

No tiene mucho sentido si los valores de la colección están deduplicados, por ejemplo:

SELECT * FROM demo9 WHERE m1 IN (SELECT DISTINCT m2 FROM demo10);

7. Cláusula GROUP BY sin función agregada y cláusula HAVING

Cuando no hay funciones y HAVINGcláusulas agregadas, GROUP BYla cláusula es solo una decoración, como esta:

SELECT * FROM demo9 WHERE m1 IN (SELECT m2 FROM demo10 GROUP BY m2);

Para estas declaraciones redundantes, el optimizador de consultas las elimina al principio.

8. No está permitido agregar, eliminar o modificar registros de una determinada tabla en una declaración mientras se realiza una subconsulta en la tabla.

Digamos esto:

mysql> DELETE FROM demo9 WHERE m1 < (SELECT MAX(m1) FROM demo9);
ERROR 1093 (HY000): You can't specify target table 'demo9' for update in FROM clause

3.2 Cómo se ejecutan las subconsultas en MySQL

Bueno, hemos revisado la sintaxis básica de la subconsulta lo más rápido posible. Si desea saber más sobre la sintaxis, puede consultar la documentación de MySQL. Ahora supongamos que todos entienden lo que es una subconsulta. Oh, a continuación hablaré sobre cómo se ejecuta cierto tipo de subconsulta en MySQL. Me emociono un poco cuando pienso en ello~ Por supuesto, para el buen desarrollo de la historia, nuestro ejemplo también debe seguir la situación. Para cambiar el arma, todavía tenemos para sacrificar s1las tablas y s2tablas que usamos en el artículo que lo lleva a comprender la optimización basada en costos de MySQL.

mysql> create table s1 (    
id int not null auto_increment,    
key1 varchar(100),    
key2 int,    
key3 varchar(100),    
key_part1 varchar(100),    
key_part2 varchar(100),    
key_part3 varchar(100),    
common_field varchar(100), 
primary key (id),
key idx_key1 (key1),    
unique key idx_key2 (key2),    
key idx_key3 (key3),    
key idx_key_part(key_part1, key_part2, key_part3));
Query OK, 0 rows affected (0.04 sec)

mysql> create table s2 (    
id int not null auto_increment,    
key1 varchar(100),    
key2 int,    
key3 varchar(100),    
key_part1 varchar(100),    
key_part2 varchar(100),    
key_part3 varchar(100),    
common_field varchar(100), 
primary key (id),
key idx_key1 (key1),    
unique key idx_key2 (key2),    
key idx_key3 (key3),    
key idx_key_part(key_part1, key_part2, key_part3));
Query OK, 0 rows affected (0.04 sec)

mysql> insert into s1 select * from demo8;
Query OK, 20000 rows affected (0.83 sec)
Records: 20000  Duplicates: 0  Warnings: 0

mysql> insert into s2 select * from demo8;
Query OK, 20000 rows affected (0.89 sec)
Records: 20000  Duplicates: 0  Warnings: 0

Las estructuras de las dos tablas s1 y s2 son las mismas, y hay 20,000 registros en las dos tablas, y se insertan valores aleatorios en las otras columnas, excepto en la columna id. Comencemos oficialmente nuestra actuación a continuación.

3.2.1 El método de ejecución de la subconsulta a los ojos de los novatos

Cuando era un adolescente puramente ignorante, pensé que la subconsulta se ejecutaba así:

  • Si la subconsulta es una subconsulta no correlacionada, como la siguiente consulta:SELECT * FROM s1WHERE key1 IN (SELECT common_field FROM s2)

    Cuando era joven, esta consulta se ejecutaba así:

    • (SELECT common_field FROM s2)Ejecute esta subconsulta solo primero
    • Luego use el resultado de la subconsulta en el paso anterior como parámetro de la consulta externa y luego ejecute la consulta externa SELECT * FROM s1 WHERE key1 IN (…).
  • Si la subconsulta es una subconsulta correlacionada, como la siguiente consulta:SELECT * FROM s1 WHERE key1 IN (SELECT common_field FROM s2 WHERE s1.key2 = s2.key2)

    s1.key2 = s2.key2La presencia de tal condición en la subconsulta en esta consulta significa que la ejecución de la subconsulta depende del valor de la consulta externa, así que cuando era joven, pensaba que la ejecución de esta consulta era así:

    • Obtenga primero un registro de la consulta externa, en este caso, s1obtenga primero un registro de la tabla
    • Luego busque el valor involucrado en la subconsulta del registro obtenido en el paso anterior, en este caso s1busque el valor de la columna del registro obtenido en la tabla s1.key2, y luego ejecute la subconsulta
    • Finalmente, de acuerdo con los resultados de la consulta de la subconsulta, se verifica si la condición de la cláusula WHERE de la consulta externa es verdadera, si es verdadera, el registro de la consulta externa se agrega al conjunto de resultados, de lo contrario, se descarta. .
    • Vuelva a ejecutar el primer paso para obtener los registros en la segunda consulta externa, y así sucesivamente~

No me digas que es solo una persona la que piensa así~

De hecho, MySQL ha pensado en una serie de métodos para optimizar la ejecución de subconsultas, en la mayoría de los casos estas medidas de optimización son realmente bastante efectivas, pero a veces fallan en asegurar que no sean uniformes, a continuación hablaremos de cómo ejecutar varios tipos de subconsultas en detalle.

3.2.2 Métodos de ejecución de subconsultas escalares y subconsultas de fila

A menudo usamos subconsultas escalares o subconsultas de fila en los siguientes dos escenarios:

  • SELECTcláusula, la subconsulta en la lista de consultas que mencionamos anteriormente debe ser una subconsulta escalar
  • Una subconsulta utiliza =、>、<、>=、<=、<>、!=、<=>el operador etc y un operando para formar una expresión booleana. Dicha subconsulta debe ser una subconsulta escalar o una subconsulta de fila.

不相关Para las ( 注意是不相关不相关不相关) subconsultas escalares o subconsultas de fila en los dos escenarios anteriores , sus métodos de ejecución son simples. Por ejemplo, la siguiente instrucción de consulta:

SELECT * FROM s1 WHERE key1 = (SELECT common_field FROM s2 WHERE key3 = 'a' LIMIT 1);

Se ejecuta de la forma en que mi yo adolescente pensó que sería:

  • (SELECT common_field FROM s2 WHERE key3 = 'a' LIMIT 1)Ejecute esta subconsulta solo primero
  • Luego use el resultado obtenido por la subconsulta en el paso anterior como parámetro de la consulta externa y luego ejecute la consulta externaSELECT * FROM s1 WHERE key1 = ...

En otras palabras, 对于包含不相关的标量子查询或者行子查询的查询语句来说,MySQL会分别独立的执行外层查询和子查询simplemente trátelo como dos consultas de una sola tabla.

Para 相关subconsultas escalares o subconsultas de fila, como la siguiente consulta:

SELECT * FROM s1 WHERE key1 = (SELECT common_field FROM s2 WHERE s1.key3 = s2.key3 LIMIT 1);

Las cosas funcionan tal como pensaba mi yo más joven, y así es como funciona:

  • Obtenga primero un registro de la consulta externa, en este caso, s1obtenga primero un registro de la tabla
  • Luego busque el valor involucrado en la subconsulta del registro obtenido en el paso anterior, en este caso s1busque el valor de la columna del registro obtenido en la tabla s1.key3, y luego ejecute la subconsulta
  • Finalmente, de acuerdo con el resultado de la consulta de la subconsulta, se verifica WHEREsi la condición de la cláusula de consulta externa es verdadera, si es verdadera, el registro de la consulta externa se agrega al conjunto de resultados, de lo contrario, se descarta.
    Vuelva a ejecutar el primer paso para obtener los registros en la segunda consulta externa, y así sucesivamente~

Es decir, no hay nada nuevo sobre el método de ejecución del optimizador de MySQL en los dos escenarios de uso de subconsultas escalares y subconsultas de fila que molestaban al principio.

3.3 Optimización de subconsultas IN

3.3.1 Propuesta de tabla materializada

Para INsubconsultas no correlacionadas, como esta:

SELECT * FROM s1 WHERE key1 IN (SELECT common_field FROM s2 WHERE key3 = 'a');

Nuestro sentimiento inicial es que esta INsubconsulta no correlacionada es la misma que la subconsulta escalar no correlacionada o la subconsulta de fila, y la consulta externa y la subconsulta se tratan como dos consultas independientes de una sola tabla, pero es una pena que MySQL haya dedicado demasiado esfuerzo INa optimizar subconsultas (después de todo, INlas subconsultas son las subconsultas más utilizadas en nuestra vida diaria), por lo que todo el proceso de ejecución no es tan simple como imaginamos ~

De hecho, para ser honesto, para INsubconsultas no relacionadas, si el número de registros en el conjunto de resultados de la subconsulta es muy pequeño, es muy eficiente tratar la subconsulta y la consulta externa como dos consultas separadas de una sola tabla. si hay demasiados conjuntos de resultados después de ejecutar la subconsulta sola, causará estos problemas:

  • Hay demasiados conjuntos de resultados, que pueden no caber en la memoria~
  • Para la consulta externa, si hay demasiados conjuntos de resultados en la subconsulta, significa que hay demasiados parámetros en la cláusula IN, lo que lleva a:
    • Los índices no se pueden usar de manera efectiva y solo se pueden realizar exploraciones de tablas completas en consultas externas

    • Al realizar un escaneo completo de la tabla en la consulta externa, debido a que hay demasiados parámetros en la cláusula IN, llevará demasiado tiempo detectar si un registro coincide con los parámetros en la cláusula IN, como los parámetros en la cláusula IN Hay sólo dos:SELECT * FROM tbl_name WHERE column IN (a, b)

      Esto es equivalente a la necesidad de juzgar si tbl_namesus columnas coinciden con cada registro de la tabla . Esto no es un problema cuando hay pocos parámetros en la cláusula IN, pero cuando hay muchos parámetros en la cláusula, como este :)columncolumn = a OR column = bINSELECT * FROM tbl_name WHERE column IN (a, b, c ..., ...

      columnLuego, cada registro debe juzgar si sus columnas coinciden column = a OR column = b OR column = c OR ..., por lo que el consumo de rendimiento será mucho mayor.

Así que MySQL pensó en un truco: 不直接将不相关子查询的结果集当作外层查询的参数,而是将该结果集写入一个临时表里. El proceso de escritura en la tabla temporal es el siguiente:

  • Las columnas de la tabla temporal son las columnas del conjunto de resultados de la subconsulta

  • Los registros escritos en la tabla temporal se deduplicarán

    Decimos que INla instrucción es para juzgar si un determinado operando está en un determinado conjunto o no. Si el valor en el conjunto se repite o no, no tiene nada que ver con el resultado de toda la instrucción IN, por lo que deduplicamos los registros al escribir el conjunto de resultados en la tabla temporal Las tablas temporales se pueden hacer más pequeñas y ahorrar espacio ~

    小提示:
    ¿Cómo deduplica los registros la tabla temporal? Esto no es un asunto trivial, la tabla temporal también es una tabla, siempre que se establezca la clave principal o el índice único para todas las columnas registradas en la tabla~

  • En general, el conjunto de resultados de la subconsulta no será demasiado grande, por lo que Memoryse creará para él una tabla temporal basada en memoria que utilice un motor de almacenamiento y se creará un índice hash para la tabla.

    小提示:
    La esencia de la declaración IN es determinar si un cierto operando está en un determinado conjunto.Si los datos en el conjunto tienen un índice hash, el proceso de coincidencia es súper rápido. ¿Algunos estudiantes no saben qué es un índice hash? No me expandiré aquí, solo búscalo en línea y pregúntame si no sabes ~

    Si el conjunto de resultados de la subconsulta es muy grande y supera la variable del sistema tmp_table_sizeo max_heap_table_size, la tabla temporal cambiará a un motor de almacenamiento basado en disco para guardar los registros en el conjunto de resultados y el tipo de índice también se convertirá en un árbol B+. índice.

MySQL llama a este proceso de guardar registros en el conjunto de resultados de la subconsulta en una tabla temporal 物化(nombre en inglés: Materialize). Por conveniencia, llamaremos a la tabla temporal que almacena el conjunto de resultados de la subconsulta 物化表. Solo porque todos los registros en la tabla materializada están indexados (la tabla materializada basada en memoria tiene un índice hash y la basada en disco tiene un índice de árbol B+), se vuelve muy difícil juzgar si un operando está en el resultado de la subconsulta. establecido a través de sentencias de ejecución de índice INRápido, mejorando así el rendimiento de la sentencia de subconsulta.

3.3.2 Table-to-join materializado

¿Es este el final del asunto? También tenemos que revisar la declaración de consulta original:

SELECT * FROM s1 WHERE key1 IN (SELECT common_field FROM s2 WHERE key3 = 'a');

Después de materializar la subconsulta, asumiendo que el nombre de la tabla materializada de la subconsulta es materialized_table, y la columna del conjunto de resultados de la subconsulta almacenado en la tabla materializada m_vales , esta consulta se puede ver desde las siguientes dos perspectivas:

  • Desde la perspectiva de la tabla s1, el significado de toda la consulta es: para cada registro de la tabla s1, si el valor de la columna clave1 del registro está en la tabla materializada correspondiente a la subconsulta, el registro se agregará a la final el conjunto de resultados. Haz un dibujo para mostrar que es así:

    inserte la descripción de la imagen aquí

  • Desde la perspectiva de la tabla materializada de la subconsulta, el significado de toda la consulta es: para cada valor de la tabla materializada de la subconsulta, si el valor de la columna key1 correspondiente es igual al valor en la tabla s1, simplemente agregue estos registros a la tabla final. conjunto resultante. Haz un dibujo para mostrar que es así:

    inserte la descripción de la imagen aquí

Es decir, de hecho, la consulta anterior es equivalente a la conexión interna entre la tabla s1y la tabla materializada de la subconsulta :materialized_table

SELECT s1.* FROM s1 INNER JOIN materialized_table ON key1 = m_val;

Después de convertir a combinación interna, se vuelve interesante.El optimizador de consultas puede evaluar el costo de diferentes secuencias de combinación y seleccionar el método de consulta con el costo más bajo para ejecutar la consulta. Analicemos el costo de usar la s1tabla de consulta externa y la tabla materializada en la consulta anterior materialized_tablepara formar la combinación interna:

  • Si s1la tabla se utiliza como tabla de control, el costo total de la consulta consta de las siguientes partes:

    • El costo de materializar subconsultas
    • Costo al escanear la tabla s1
    • El número de registros en la tabla s1 × el costo del acceso de una sola tabla a la tabla (dijimos anteriormente que los registros en la tabla materializada no se repiten y los índices se construyen para las columnas en la tabla materializada, por lo que este paso es obviamente muy m_val = xxxrápidomaterialized_table
  • Si materialized_tablela tabla se utiliza como tabla de control, el costo total de la consulta consta de las siguientes partes:

    • El costo de materializar subconsultas
    • Costo al escanear tablas materializadas
    • El número de registros en la tabla materializada × el costo del acceso de una sola tabla a la tabla (afortunadamente, se establece un índice en la columna key1, por lo que este paso es muy rápido key1 = xxx)s1

El optimizador de consultas MySQL utilizará cálculos para seleccionar la solución de menor costo mencionada anteriormente para ejecutar la consulta.

3.2.3 Convertir subconsulta en semi-join

Aunque habrá un costo de crear una tabla temporal después de materializar la subconsulta antes de ejecutar la consulta, pero de todos modos, hemos visto el poderoso efecto de convertir la subconsulta en una conexión. ¿Qué pasa con las subconsultas convertidas en uniones? Repasemos la consulta anterior:

SELECT * FROM s1 WHERE key1 IN (SELECT common_field FROM s2 WHERE key3 = 'a');

Podemos entender esta consulta como: Para s1un determinado registro en la tabla, si podemos encontrar uno o más registros en s2la tabla (exactamente, el conjunto de resultados después de la ejecución ), el valor de estos registros es igual al valor del registro de la tabla valor de la columna, los registros de esta tabla se agregarán al conjunto de resultados final. Este proceso es en realidad muy similar al efecto de unir dos tablas:WHERE s2.key3 = 'a'common_fields1key1s1s1s2

SELECT s1.* FROM s1 INNER JOIN s2 ON s1.key1 = s2.common_field WHERE s2.key3 = 'a';

Es solo que no podemos garantizar que para s1un registro en la tabla, cuántos registros en s2la tabla (exactamente, el conjunto de resultados después de la ejecución ) cumplan con esta condición, pero podemos discutirlo en tres situaciones:WHERE s2.key3 = 'a's1.key1 =s2.common_field

  • 情况一:Para s1un registro en la tabla, si cualquier registro s2en la tabla 没有cumple s1.key1 = s2.common_fieldesta condición, entonces el registro naturalmente no se agregará al conjunto de resultados final.
  • 情况二: para s1un registro en la tabla, si el registro s2en la tabla 有且只有cumple s1.key1 = s2.common_fieldesta condición, entonces el registro se agregará al conjunto de resultados final
  • 情况三: para s1un registro en la tabla, si el registro s2en la tabla 至少有2条cumple s1.key1 = s2.common_fieldesta condición, entonces el registro se agregará al conjunto de resultados final varias veces

Para s1un registro en la tabla, dado que solo nos importa s2si hay registros en la tabla que cumplan con s1.key1 = s2.common_fieldesta condición, 而不关心具体有多少条记录与之匹配y porque 情况三algunos existen, INla subconsulta que mencionamos anteriormente no es completamente equivalente a la conexión entre las dos tablas. Pero convertir una subconsulta en una conexión realmente puede dar pleno juego al papel del optimizador, por lo que MySQL propone aquí un nuevo concepto - 半连接(nombre en inglés: semi-join). El significado de semi-unir la tabla s1 y la tabla s2 es: 对于s1表的某条记录来说,我们只关心在s2表中是否存在与之匹配的记录是否存在,而不关心具体有多少条记录与之匹配,最终的结果集中只保留s1表的记录. Para que todos tengan una sensación más intuitiva, asumimos que MySQL reescribe internamente la subconsulta anterior de la siguiente manera:

SELECT s1.* FROM s1 SEMI JOIN s2 ON s1.key1 = s2.common_field WHERE key3 = 'a';

小提示:
Semi-join es solo una forma de ejecutar subconsultas dentro de MySQL. MySQL no proporciona una sintaxis de semi-join orientada al usuario, por lo que no necesitamos, y no podemos intentar poner la declaración anterior en un marco negro para ejecutar. Solo quiero explicar que la subconsulta anterior se convertirá en una semi-unión similar a la declaración anterior dentro de MySQL ~

El concepto está ahí, ¿cómo realizar este supuesto 半连接? MySQL ha preparado varios métodos

1. Table pullout (table pullout en subconsulta)

En ese momento子查询的查询列表处只有主键或者唯一索引列 , puede agregar directamente las tablas en la subconsulta 上拉a las cláusulas de la consulta externa FROMy fusionar las condiciones de búsqueda en la subconsulta con las condiciones de búsqueda de la consulta externa, como esta:

SELECT * FROM s1 WHERE key2 IN (SELECT key2 FROM s2 WHERE key3 = 'a');

Dado que key2la columna es la columna s2de la tabla 唯一二级索引, podemos extraer directamente s2la tabla en la cláusula de la consulta externa FROMy fusionar las condiciones de búsqueda en la subconsulta con las condiciones de búsqueda de la consulta externa. La consulta después de la extracción es así:

SELECT s1.* FROM s1 INNER JOIN s2 ON s1.key2 = s2.key2 WHERE s2.key3 = 'a';

P1: ¿Por qué una subconsulta se puede convertir directamente en una consulta conjunta cuando la lista de consultas de la subconsulta solo contiene 主键o columnas?唯一索引

¡Porque los datos en la clave principal o la columna de índice único en sí no se repiten! Entonces, para los registros en la misma tabla s1, no puede encontrar más de dos registros que coincidan con s1.key2 = s2.key2~

2. Estrategia de ejecución DuplicateWeedout (eliminación de valores duplicados)

Para esta consulta:

SELECT * FROM s1 WHERE key1 IN (SELECT common_field FROM s2 WHERE key3 = 'a');

Después de convertir a una consulta semi-join, s1un registro en la tabla puede s2tener varios registros coincidentes en la tabla, por lo que el registro puede agregarse al conjunto de resultados final varias veces. Para eliminar la duplicación, podemos crear una tabla temporal. Por ejemplo, la tabla temporal se ve así:

CREATE TABLE tmp (
  id PRIMARY KEY
 );

De esta manera, en el proceso de ejecución de la consulta de conexión, cada vez que s1se va a agregar un registro en una tabla al conjunto de resultados, idel valor de este registro se agrega primero a la tabla temporal. Si la adición es exitosa, significa que el s1registro en la tabla anterior no se ha agregado al conjunto de resultados final, ahora agregue este registro al conjunto de resultados final; si la adición falla, significa que el registro en la tabla anterior ya se agregó al conjunto de resultados s1final , así que simplemente deséchelo aquí.Este método de usar una tabla temporal para eliminar semi-joinvalores duplicados en el conjunto de resultados se llama Duplicate Weedout.

3. Estrategia de ejecución de LooseScan (escaneo de índice suelto)

Echa un vistazo a esta consulta:

SELECT * FROM s1 WHERE key3 IN (SELECT key1 FROM s2 WHERE key1 > 'a' AND key1 < 'b');

En la subconsulta, s2el acceso a la tabla puede usar key1el índice de la columna, y la lista de consultas de la subconsulta pasa a ser key1la columna, de modo que después de que la consulta se convierta en una consulta semi-join, si s2la consulta se ejecutará como la tabla de conducción, luego ejecute El proceso es así:

inserte la descripción de la imagen aquí

Como se muestra en la figura, en el índice s2de la tabla , hay 2 registros de índice secundario con el valor de, por lo que solo necesita tomar el valor del primero para encontrar el registro de ' en la tabla , si puede encontrar el registro correspondiente en la tabla, luego agregue el registro correspondiente al conjunto de resultados. Por analogía, otros registros de índice secundario con el mismo valor solo necesitan tomar el valor del primer registro para encontrar un registro coincidente en la tabla.Aunque se trata de un índice de exploración, solo el primer registro del mismo valor se toma en The way para hacer la operación de coincidencia se llama .idx_key1'aa's1s1.key3 = 'aas1s1松散索引扫描

4. Estrategia de ejecución de materialización semi-join

Lo que introdujimos antes INmaterializa las subconsultas irrelevantes en las cláusulas de la consulta externa, y luego la conexión entre la tabla de consulta externa y la tabla materializada es esencialmente una unión parcial, pero debido a que la tabla materializada no tiene registros duplicados, puede convertir directamente la subconsulta en una consulta de combinación.

5. Estrategia de ejecución de FirstMatch (primer partido)

FirstMatchEs el método de ejecución semi-join más primitivo, que es el mismo que el método de ejecución de subconsultas correlacionadas que pensábamos cuando éramos jóvenes, es decir, primero busca un registro en la consulta externa y luego encuentra una coincidencia. en el registro condicional de la tabla de subconsulta, si puede encontrar uno, coloque el registro de la consulta externa en el conjunto de resultados final y deje de buscar más registros coincidentes, si no los encuentra, descarte el registro de la consulta externa y luego comience a tomar la siguiente registro en la consulta externa y repita el proceso anterior.

Para algunas subconsultas correlacionadas que usan la instrucción IN, como esta consulta:

SELECT * FROM s1 WHERE key1 IN (SELECT common_field FROM s2 WHERE s1.key3 = s2.key3);

También se puede convertir fácilmente en una semi-unión, y la declaración convertida se ve así:

SELECT s1.* FROM s1 SEMI JOIN s2 ON s1.key1 = s2.common_field AND s1.key3 = s2.key3;

Luego, puede usar la estrategia de ejecución semi-join que presentamos anteriormente para ejecutar la consulta. Por supuesto, si solo DuplicateWeedouthay claves principales o columnas de índice secundarias únicas LooseScanen FirstMatchla lista de consultas de la subconsulta, también puede usar directamente la estrategia table pulloutpara ejecutar la consulta. consulta, pero Lo que necesita la atención de todos es eso 由于相关子查询并不是一个独立的查询,所以不能转换为物化表来执行查询.

3.2.4 Condiciones aplicables de semi-unión

Por supuesto, no todas INlas declaraciones de consulta que contienen subconsultas se pueden convertir a semi-join, solo las consultas como esta se pueden convertir a semi-join:

SELECT ... FROM outer_tables WHERE expr IN (SELECT ... FROM inner_tables ...) AND ...

O algo como esto funcionaría:

SELECT ... FROM outer_tables WHERE (oe1, oe2, ...) IN (SELECT ie1, ie2, ... FROM inner_tables ...) AND ...

Para resumir en palabras, solo las subconsultas que cumplan las siguientes condiciones se pueden convertir a semi-join:

  • La subconsulta debe ser INuna expresión booleana que consta de una instrucción AND y debe aparecer en la cláusula WHEREOR de la consulta externa.ON
  • La consulta externa también puede tener otras condiciones de búsqueda, pero debe estar conectada con INlas condiciones de búsqueda de la subconsultaAND
  • La subconsulta debe ser una sola consulta y no puede ser UNIONconcatenada por varias consultas
  • La subconsulta no puede contener declaraciones GROUP BYOR HAVINGo funciones agregadas
  • … Todavía hay algunas condiciones que son relativamente raras, así que no voy a molestar~

3.2.5 No apto para semi-unión

Para algunas situaciones en las que la subconsulta no se puede transponer a una semi-unión, los ejemplos típicos incluyen lo siguiente:

1. Hay otras condiciones de búsqueda en la condición WHERE de la consulta externa y la expresión booleana compuesta por la subconsulta IN está conectada por OR

SELECT * FROM s1 WHERE key1 IN (SELECT common_field FROM s2 WHERE key3 = 'a')  OR key2 > 100;

2. Casos en los que se usa NOT IN en lugar de IN

SELECT * FROM s1 WHERE key1 NOT IN (SELECT common_field FROM s2 WHERE key3 = 'a')

3. El caso de las subconsultas IN en la cláusula SELECT

SELECT key1 IN (SELECT common_field FROM s2 WHERE key3 = 'a') FROM s1 ;

4. Cuando la subconsulta contiene funciones GROUP BY, HAVING o agregadas

SELECT * FROM s1 WHERE key2 IN (SELECT COUNT(*) FROM s2 GROUP BY key1);

5. El caso en el que se incluye UNION en la subconsulta

SELECT * FROM s1 WHERE key1 IN (
    SELECT common_field FROM s2 WHERE key3 = 'a'
    UNION
    SELECT common_field FROM s2 WHERE key3 = 'b'
);

MySQL仍然留了两口绝活来优化不能转为semi-joinLa subconsulta de la consulta, es decir:

1. Para subconsultas no correlacionadas, puede intentar materializarlas antes de participar en la consulta

Por ejemplo, la consulta que mencionamos anteriormente:

SELECT * FROM s1 WHERE key1 NOT IN (SELECT common_field FROM s2 WHERE key3 = 'a')

Materializar primero la subconsulta y luego juzgar si key1 está en el conjunto de resultados de la tabla materializada puede acelerar la ejecución de la consulta.

小提示:
Tenga en cuenta que una vez que la subconsulta se materializa aquí, no se puede convertir en una conexión con la tabla de consulta externa. Solo puede escanear primero la tabla s1 y luego, para un registro en la tabla s1, determinar si el valor clave1 de la registro está en la tabla materializada. .

2. Independientemente de si la subconsulta está relacionada o no, puede intentar usar la subconsulta IN como una subconsulta EXISTS

De hecho, cualquier subconsulta IN se puede convertir en una subconsulta EXISTS Los ejemplos generales son los siguientes:

outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where)

se puede convertir a:

EXISTS (SELECT inner_expr FROM ... WHERE subquery_where AND outer_expr=inner_expr)

Por supuesto, hay algunos casos especiales en este proceso, como el caso en el que outer_exprel inner_exprvalor o NULLes especial. Debido a que NULLlas expresiones con valores como operandos a menudo dan como resultado NULL, digamos:

mysql> SELECT NULL IN (1, 2, 3);
+-------------------+
| NULL IN (1, 2, 3) |
+-------------------+
|              NULL |
+-------------------+
1 row inset (0.00 sec)


mysql> SELECT 1 IN (1, 2, 3);
+----------------+
| 1 IN (1, 2, 3) |
+----------------+
|              1 |
+----------------+
1 row inset (0.00 sec)

mysql> SELECT NULL IN (NULL);
+----------------+
| NULL IN (NULL) |
+----------------+
|           NULL |
+----------------+
1 row inset (0.00 sec)

Y el resultado de la subconsulta EXISTS debe TRUEser FASLE:

mysql> SELECT EXISTS (SELECT 1 FROM s1 WHERE NULL = 1);
+------------------------------------------+
| EXISTS (SELECT 1 FROM s1 WHERE NULL = 1) |
+------------------------------------------+
|                                        0 |
+------------------------------------------+
1 row inset (0.01 sec)

mysql> SELECT EXISTS (SELECT 1 FROM s1 WHERE 1 = NULL);
+------------------------------------------+
| EXISTS (SELECT 1 FROM s1 WHERE 1 = NULL) |
+------------------------------------------+
|                                        0 |
+------------------------------------------+
1 row inset (0.00 sec)

mysql> SELECT EXISTS (SELECT 1 FROM s1 WHERE NULL = NULL);
+---------------------------------------------+
| EXISTS (SELECT 1 FROM s1 WHERE NULL = NULL) |
+---------------------------------------------+
|                                           0 |
+---------------------------------------------+
1 row inset (0.00 sec)

Pero, afortunadamente, la mayoría de los escenarios en los que usamos subconsultas INson para ponerlo en WHEREla cláusula o ON, y la cláusula WHEREo ONes indistinguible , por ejemplo:NULLFALSE

mysql> SELECT 1 FROM s1 WHERE NULL;
Empty set (0.00 sec)

mysql> SELECT 1 FROM s1 WHERE FALSE;
Empty set (0.00 sec)

Entonces, siempre que nuestra subconsulta IN se coloque en la cláusula WHEREo ON, entonces IN -> EXISTSla conversión no es un problema. Habiendo dicho todo eso, ¿por qué cambiar? Esto se debe a que el índice no se puede usar sin conversión, como la siguiente consulta:

SELECT * FROM s1 WHERE 
key1 IN (SELECT key3 FROM s2 where s1.common_field = s2.common_field) 
	 OR key2 > 1000;

La subconsulta en esta consulta es una subconsulta correlacionada y el índice no se puede usar cuando se ejecuta la subconsulta, pero el índice EXISTSse puede usar después de convertirlo en una subconsulta:

SELECT * FROM s1 WHERE EXISTS (SELECT 1 FROM s2 wheres1.common_field = s2.common_field AND s2.key3 = s1.key1)   OR key2 > 1000;

El índice de la tabla EXISTSse puede utilizar al convertir a una subconsulta .s2idx_key3

Cabe señalar que si INla subconsulta no cumple semi-joinlas condiciones para la conversión y no se puede convertir en una tabla materializada o el costo de conversión en una tabla materializada es demasiado alto, entonces se convertirá en una consulta EXISTS.

小提示:
Cuando MySQL 5.5 y versiones anteriores no introdujeron métodos de semi-unión y materialización para optimizar las subconsultas, el optimizador convertía las subconsultas IN en subconsultas EXISTS. Muchos estudiantes exclamaron que lo que escribí era una subconsulta irrelevante. ¿Por qué debería ejecutarse en el mismo manera como una subconsulta correlacionada? Entonces, en ese momento, muchas voces sugirieron convertir subconsultas en uniones. Sin embargo, con el desarrollo de MySQL, se han introducido muchas estrategias de optimización de subconsultas en versiones recientes. Puede usar subconsultas con un poco de confianza. El trabajo de conversión interna El El optimizador lo hace automáticamente para todos.

Resumir

Si INla subconsulta cumple con las condiciones de conversión semi-join, el optimizador de consultas primero convertirá la subconsulta en una semi-unión y luego considerará cuál de las siguientes cinco estrategias de ejecución de semi-unión tiene el costo más bajo:

  • Mesa extraíble
  • DuplicateWeedout
  • Materialización LooseScan
  • FirstMatch
    elige la estrategia de ejecución con el costo más bajo para ejecutar la subconsulta.

Si INla subconsulta no cumple semi-joinlas condiciones para la conversión, el optimizador de consultas encontrará una forma más económica de ejecutar la subconsulta a partir de las siguientes dos estrategias:

  • Materializar la subconsulta antes de ejecutar la consulta
  • Realice la conversión IN a EXISTS.

3.4 Optimización de subconsultas ANY/ALL

Si las subconsultas ANY/ALL son subconsultas no correlacionadas, se pueden convertir en formas familiares de ejecución en muchas ocasiones, por ejemplo:

expresión cruda convertido a
< CUALQUIERA (SELECCIONE expr_interna …) < (SELECCIONE MAX(inner_expr) …)
> CUALQUIERA (SELECCIONE expr_interior…) > (SELECCIONE MIN(inner_expr) …)
< TODO (SELECCIONE expr_interior …) < (SELECCIONE MIN(expresión_interna) …)
> TODO (SELECCIONE inner_expr …) > (SELECCIONE MAX(inner_expr) …)

3.5 Ejecución de la subconsulta [NO] EXISTE

Si [NOT] EXISTSla subconsulta es una subconsulta no correlacionada, puede ejecutar la subconsulta primero para averiguar si [NOT] EXISTSel resultado de la subconsulta es TRUEo FALSEy volver a escribir la declaración de consulta original. Por ejemplo, para esta consulta:

SELECT * FROM s1 WHERE EXISTS (SELECT 1 FROM s2 WHERE key1 = 'a')  OR key2 > 100;

Debido a que la subconsulta en esta declaración es una subconsulta irrelevante, el optimizador ejecutará la subconsulta primero, asumiendo que el resultado de la subconsulta EXISTS es TRUE, luego el optimizador reescribirá la consulta como:

SELECT * FROM s1  WHERE TRUE OR key2 > 100;

Después de una mayor simplificación, se convierte en:

SELECT * FROM s1 WHERE TRUE;

Para una subconsulta [NOT] EXISTS correlacionada, como esta consulta:

SELECT * FROM s1
    WHERE EXISTS (SELECT 1 FROM s2 WHERE s1.common_field = s2.common_field);

Desafortunadamente, esta consulta solo se puede ejecutar de la forma en que hacíamos las subconsultas correlacionadas cuando éramos jóvenes. Sin embargo, si el índice se puede utilizar en la subconsulta [NOT] EXISTS, la velocidad de la consulta será mucho más rápida, por ejemplo:

SELECT * FROM s1  WHERE EXISTS (SELECT 1 FROM s2 WHERE s1.common_field = s2.key1);

La subconsulta EXISTS anterior puede usar idx_key1 para acelerar la consulta.

3.6 Optimización para tablas derivadas

Dijimos antes que la subconsulta se coloca FROMdespués de la cláusula de la consulta externa, entonces el resultado de esta subconsulta es equivalente a una tabla derivada, como la siguiente consulta:

SELECT * FROM  (
        SELECT id AS d_id,  key3 AS d_key3 FROM s2 WHERE key1 = 'a'
    ) AS derived_s1 WHERE d_key3 = 'a';

El resultado de la subconsulta ( SELECT id AS d_id, key3 AS d_key3 FROM s2 WHERE key1 = 'a')es equivalente a una tabla derivada, el nombre de esta tabla es derived_s1, la tabla tiene dos columnas, a saber d_idyd_key3

Para consultas que contienen tablas derivadas, MySQL ofrece dos estrategias de ejecución:

1. Lo más fácil de pensar es materializar la tabla derivada

Podemos escribir el conjunto de resultados de la tabla derivada en una tabla temporal interna y luego usar esta tabla materializada como una tabla normal para participar en la consulta. Por supuesto, al materializar la tabla derivada, MySQL usa una estrategia llamada materialización retrasada, es decir, retrocede e intenta materializar la tabla derivada cuando la tabla derivada se usa realmente en la consulta, en lugar de simplemente comenzar a ejecutar la consulta antes. comienza La tabla derivada se materializa. Por ejemplo, para la siguiente consulta con una tabla derivada:

SELECT * FROM (
        SELECT * FROM s1 WHERE key1 = 'a'
    ) AS derived_s1 INNER JOIN s2
    ON derived_s1.key1 = s2.key1
    WHERE s2.key2 = 1;

Si la consulta se ejecuta en forma de una tabla derivada materializada, la ejecución primero encontrará s2un registro satisfactorio en la tabla s2.key2 = 1. Si no se encuentra, significa que el registro en la tabla s2 que participa en la conexión está vacío. , por lo que el resultado de toda la consulta El conjunto está vacío, por lo que no es necesario materializar la tabla derivada en la consulta.

2. Combinar la tabla derivada con la tabla externa, es decir, reescribir la consulta en un formulario sin tabla derivada

Veamos esta consulta simple que involucra tablas derivadas:

SELECT * FROM (SELECT * FROM s1 WHERE key1 = 'a') AS derived_s1;

Esta consulta esencialmente quiere ver todos los registros s1de la tabla que cumplen key1 = 'a'las condiciones, por lo que es equivalente a la siguiente declaración:

SELECT * FROM s1 WHERE key1 = 'a';

Para algunas declaraciones un poco más complejas que involucran tablas derivadas, como la que mencionamos anteriormente:

SELECT * FROM (
        SELECT * FROM s1 WHERE key1 = 'a'
    ) AS derived_s1 INNER JOIN s2
    ON derived_s1.key1 = s2.key1
    WHERE s2.key2 = 1;

Podemos fusionar la tabla derivada con la tabla de la consulta externa y luego colocar las condiciones de búsqueda en la tabla derivada en las condiciones de búsqueda de la consulta externa, así:

SELECT * FROM s1 INNER JOIN s2
    ON s1.key1 = s2.key1
    WHERE s1.key1 = 'a' AND s2.key2 = 1;

De esta forma, la tabla derivada se elimina con éxito fusionando la consulta externa con la tabla derivada, lo que significa que ya no tenemos que pagar el costo de crear y acceder a la tabla temporal. Sin embargo, no todas las consultas con tablas derivadas se pueden fusionar correctamente con la consulta externa. Cuando hay estas declaraciones en la tabla derivada, no se pueden fusionar con la consulta externa:

  • Funciones agregadas, como MAX(), MIN(), SUM(), etc.
  • DISTINTO
  • AGRUPAR POR
  • TENIENDO
  • LÍMITE
  • UNIÓN o UNIÓN TODOS
  • La cláusula SELECT de la subconsulta correspondiente a la tabla derivada contiene otra subconsulta
  • ... Todavía hay algunas situaciones poco comunes, así que no diré más~

Por lo tanto, cuando MySQL se ejecuta con una tabla derivada, primero intenta fusionar la tabla derivada con la consulta externa, si falla, materializa la tabla derivada y ejecuta la consulta.

Hasta ahora, el estudio de hoy ha terminado, espero que te conviertas en un yo indestructible
~~~

No puedes conectar los puntos mirando hacia adelante; solo puedes conectarlos mirando hacia atrás. Así que tienes que confiar en que los puntos se conectarán de alguna manera en tu futuro. Tienes que confiar en algo: tu instinto, destino, vida, karma, lo que sea. Este enfoque nunca me ha defraudado y ha marcado una gran diferencia en mi vida.

Si mi contenido te es útil, por favor 点赞, 评论,, 收藏la creación no es fácil, el apoyo de todos es la motivación para que yo persevere

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/liang921119/article/details/130824724
Recomendado
Clasificación