Operación de la función de campo de condición, conversión de tipo implícita, conversión de codificación de caracteres implícita en la consulta de selección

Operación de la función del campo de condición

Piensa en ejemplos:

mysql> CREATE TABLE `tradelog` (
  `id` int(11) NOT NULL,
  `tradeid` varchar(32) DEFAULT NULL,
  `operator` int(11) DEFAULT NULL,
  `t_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `tradeid` (`tradeid`),
  KEY `t_modified` (`t_modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Ahora que se han registrado todos los datos desde principios de 2016 hasta finales de 2018, el departamento de operaciones tiene el requisito de contar el número total de registros de transacciones que ocurrieron en julio en todos los años.

mysql> seleccione el recuento (*) del registro comercial donde mes (t_modified) = 7;

Debido a que hay un índice en el campo t_modified, puede ejecutar esta declaración de manera segura en la biblioteca de producción, pero descubre que tomó mucho tiempo ejecutarla antes de devolver el resultado.

La razón es que el cálculo de la función se realiza en el campo y no se usa el índice. Entonces, ¿por qué se puede usar el índice cuando la condición es donde t_modified = '2018-7-1', pero no cuando se cambia a donde mes (t_modified) = 7?

De hecho, si calcula la función mes (), verá que cuando pasa en 7, no sabe qué hacer en el primer nivel del árbol. Por lo tanto, realizar operaciones funcionales en campos de índice puede destruir el orden de los valores de índice, por lo que el optimizador decide abandonar la función de búsqueda de árbol. Cabe señalar que el optimizador no quiere dejar de usar este índice.

En este ejemplo, se abandona la función de búsqueda de árbol. El optimizador puede optar por recorrer el índice de clave principal o el índice t_modified. Después de comparar el tamaño del índice, el optimizador encuentra que el índice t_modified es más pequeño y atravesar este índice es más rápido que atravesar el índice de clave principal. Por lo tanto, eventualmente se seleccionará el índice t_modified.

key = "t_modified" significa que se usa el índice t_modified; inserté 100,000 filas de datos en los datos de la tabla de prueba, filas = 100335, lo que indica que esta declaración escaneó todos los valores de todo el índice; el índice de uso del Extra campo, Indica que se utiliza el índice de cobertura.

Finalmente, debido a la adición de la operación de la función month (), MySQL ya no puede usar la función de posicionamiento rápido de índice, pero solo puede usar el escaneo de índice completo . Incluso para las funciones que no cambian el orden, no se consideran los índices . Por ejemplo, para la instrucción SQL seleccione * de tradelog donde id + 1 = 10000, esta operación de adición no cambia el orden, pero el optimizador de MySQL aún no puede localizar rápidamente la fila 9999 usando el índice de id.

Conversión de tipo implícita

mysql> select * from tradelog where tradeid=110717;

El tipo de campo de tradeid es varchar (32), pero el parámetro de entrada es un número entero, por lo que se requiere conversión de tipo.

Reglas de conversión en MySQL : en MySQL, cuando una cadena se compara con un número, la cadena se convierte en un número .

Entonces la consulta anterior es equivalente a:

mysql> select * from tradelog where  CAST(tradid AS signed int) = 110717;

Se convierte en la operación de función de campo condicional mencionada anteriormente.

Conversión de codificación de caracteres implícita

Suponga que hay otra tabla trade_detail en el sistema, que se utiliza para registrar los detalles de la operación de la transacción. Para facilitar el análisis cuantitativo y la recurrencia, inserté algunos datos en las dos tablas, tradelog y trade_detail.

mysql> CREATE TABLE `trade_detail` (
  `id` int(11) NOT NULL,
  `tradeid` varchar(32) DEFAULT NULL,
  `trade_step` int(11) DEFAULT NULL, /*操作步骤*/
  `step_info` varchar(32) DEFAULT NULL, /*步骤信息*/
  PRIMARY KEY (`id`),
  KEY `tradeid` (`tradeid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into tradelog values(1, 'aaaaaaaa', 1000, now());
insert into tradelog values(2, 'aaaaaaab', 1000, now());
insert into tradelog values(3, 'aaaaaaac', 1000, now());
insert into trade_detail values(1, 'aaaaaaaa', 1, 'add');
insert into trade_detail values(2, 'aaaaaaaa', 2, 'update');
insert into trade_detail values(3, 'aaaaaaaa', 3, 'commit');
insert into trade_detail values(4, 'aaaaaaab', 1, 'add');
insert into trade_detail values(5, 'aaaaaaab', 2, 'update');
insert into trade_detail values(6, 'aaaaaaab', 3, 'update again');
insert into trade_detail values(7, 'aaaaaaab', 4, 'commit');
insert into trade_detail values(8, 'aaaaaaac', 1, 'add');
insert into trade_detail values(9, 'aaaaaaac', 2, 'update');
insert into trade_detail values(10, 'aaaaaaac', 3, 'update again');
insert into trade_detail values(11, 'aaaaaaac', 4, 'commit');

Haga la consulta: mysql> seleccione d. * De tradelog l, trade_detail d donde d.tradeid = l.tradeid y l.id = 2; / * Declaración Q1 * /

  • La primera línea muestra que el optimizador primero encontrará la fila con id = 2 en el registro comercial de la tabla de registro de transacciones.Este paso usa el índice de clave principal, las filas = 1 significa que solo escanea una fila;
  • La segunda línea, key = NULL, significa que no se utiliza el índice tradeid en la tabla de detalles comerciales trade_detail, y se realiza un escaneo completo de la tabla.

Llamamos tradelog la tabla impulsada, trade_detail la tabla impulsada y tradeid el campo asociado . Hay un índice en el campo tradeid en la tabla trade_detail. Originalmente esperábamos ubicar rápidamente la fila equivalente usando el índice tradeid. Pero no está aquí.

El problema aquí es que los conjuntos de caracteres son inconsistentes. Una de las dos tablas es utf8mb4 y la otra es utf8. El conjunto de caracteres utf8mb4 es un superconjunto de utf8. Por lo tanto, cuando se comparan estos dos tipos de cadenas, el funcionamiento interno de MySQL es: Convierta la cadena utf8 al juego de caracteres utf8mb4 y luego compare.

En el lenguaje de programación, cuando se realiza una conversión automática de tipos, para evitar errores de datos causados ​​por el truncamiento durante el proceso de conversión, la conversión se realiza "en la dirección de aumentar la longitud de los datos".

La consulta de selección anterior se convierte en: select * from trade_detail donde CONVERT (traideid USING utf8mb4) = $ L2.tradeid.value; 

Esto una vez más activa el principio que mencionamos anteriormente: para realizar operaciones de función en el campo de índice, el optimizador abandonará la función de búsqueda de árbol. La diferencia en el juego de caracteres es solo una de las condiciones El requisito de agregar operaciones de función en el campo de índice de la tabla conducida durante el proceso de conexión conduce directamente al escaneo completo de la tabla conducida.

Como verificación de comparación, cambie el requisito a "Encuentre la operación con id = 4 en la tabla trade_detail, quién es el operador correspondiente", y luego observe esta declaración y su plan de ejecución.

mysql> seleccione l.operator de tradelog l, trade_detail d donde d.tradeid = l.tradeid y d.id = 4;

En esta declaración, la tabla trade_detail se convierte en la tabla inicial, pero la segunda línea del resultado de la explicación muestra que esta operación de consulta usa el índice (tradeid) en el registro comercial de la tabla conducida, y el número de filas escaneadas es 1.

Esta declaración es equivalente a: seleccione el operador del registro comercial donde traideid = $ R4.tradeid.value;

El conjunto de caracteres de $ R4.tradeid.value es utf8. De acuerdo con las reglas de conversión del conjunto de caracteres, debe convertirse a utf8mb4, por lo que este proceso se reescribe como:

seleccione el operador del registro comercial donde traideid = CONVERT ($ R4.tradeid.value USANDO utf8mb4); 

Entonces, para optimizar la declaración Q1, hay dos enfoques:

  • Un método de optimización más común es cambiar el juego de caracteres del campo tradeid en la tabla trade_detail a utf8mb4, para que no haya problemas de conversión del juego de caracteres.
alter table trade_detail modify tradeid varchar(32) CHARACTER SET utf8mb4 default null;
  • Es mejor si puede modificar el juego de caracteres del campo. Pero si la cantidad de datos es relativamente grande, o si el DDL no se puede realizar en la empresa temporalmente, entonces la única forma de modificar la declaración SQL es mediante el uso.
mysql> select d.* from tradelog l , trade_detail d where d.tradeid=CONVERT(l.tradeid USING utf8) and l.id=2; 

En conclusión

Se mencionan tres ejemplos, que en realidad hablan de lo mismo, es decir: realizar operaciones funcionales en campos de índice puede destruir el orden de los valores de índice, por lo que el optimizador decidió abandonar la función de búsqueda de árbol.

 

Fuente del contenido: Lin Xiaobin "45 conferencias sobre combate real de MySQL"

Supongo que te gusta

Origin blog.csdn.net/qq_24436765/article/details/112658031
Recomendado
Clasificación