Me enfrenté de nuevo, la conversión implícita de MySQL creó un error en línea

Un día, el desarrollador me preguntó por qué hay dos registros para una consulta y uno de los registros que no cumple la condición select * from tablea where xxno = 170325171202362928;xxnoes  170325171202362928 y  170325171202362930aparece en los resultados.

¿Por qué una consulta equivalente tiene otro registro con un valor diferente?

¡Vamos a ver!

análisis

Verificamos la estructura de la tabla y encontramos que xxno es un varchar tipo, pero el lado derecho del signo igual es un tipo numérico ¿Cómo manejará MySQL esta situación? El documento oficial es el siguiente: https://dev.mysql.com/doc/refman/5.6/en/type-conversion.html

Las siguientes reglas describen cómo se produce la conversión para las operaciones de comparación: .... 省略 一 万 字 .... En todos los demás casos, los argumentos se comparan como números de punto flotante (reales).

En otras palabras, convertirá ambos lados del signo igual en números de punto flotante para comparar.

Las comparaciones que utilizan números de punto flotante (o valores que se convierten en números de punto flotante) son aproximadas porque dichos números son inexactos. Esto puede dar lugar a resultados que parecen inconsistentes:

Si la comparación utiliza un tipo de punto flotante, la comparación será aproximada, lo que hará que los resultados parezcan inconsistentes, lo que puede generar resultados de consulta incorrectos.

Probemos el ejemplo que acabamos de producir:

mysql > select '170325171202362928' = 170325171202362930;
+-------------------------------------------+
| '170325171202362928' = 170325171202362930 |
+-------------------------------------------+
|                                         1 |
+-------------------------------------------+
1 row in set (0.00 sec)```

Se puede encontrar que '170325171202362928' la 170325171202362930comparación de la cadena y el valor resultó ser igual. Veamos  el resultado de convertir cadenas '170325171202362928' y cadenas '170325171202362930'a punto flotante

mysql  > select '170325171202362928'+0.0;
+--------------------------+
| '170325171202362928'+0.0 |
+--------------------------+
|    1.7032517120236294e17 |
+--------------------------+
1 row in set (0.00 sec)

mysql > select '170325171202362930'+0.0;
+--------------------------+
| '170325171202362930'+0.0 |
+--------------------------+
|    1.7032517120236294e17 |
+--------------------------+
1 row in set (0.00 sec)

Descubrimos que después de convertir dos cadenas diferentes en números de punto flotante, el resultado es el mismo.

Entonces, siempre que los valores después de la conversión a números de punto flotante sean iguales, entonces la comparación después de la conversión implícita será igual, continuamos probando los resultados de otras cadenas convertidas a punto flotante iguales

mysql > select '170325171202362931'+0.0;
+--------------------------+
| '170325171202362931'+0.0 |
+--------------------------+
|    1.7032517120236294e17 |
+--------------------------+
1 row in set (0.00 sec)

mysql > select '170325171202362941'+0.0;
+--------------------------+
| '170325171202362941'+0.0 |
+--------------------------+
|    1.7032517120236294e17 |
+--------------------------+
1 row in set (0.00 sec)

Cadenas '170325171202362931'y '170325171202362941'convertidas a resultados de punto flotante, buscamos y comparamos los resultados de su valor

mysql > select '170325171202362931' = 170325171202362930;
+-------------------------------------------+
| '170325171202362931' = 170325171202362930 |
+-------------------------------------------+
|                                         1 |
+-------------------------------------------+
1 row in set (0.00 sec)

mysql > select '170325171202362941' = 170325171202362930;
+-------------------------------------------+
| '170325171202362941' = 170325171202362930 |
+-------------------------------------------+
|                                         1 |
+-------------------------------------------+
1 row in set (0.00 sec)

El resultado también está en línea con las expectativas.

Por lo tanto, cuando MySQL encuentra una falta de coincidencia de tipo de campo, realizará varias conversiones implícitas, tenga cuidado, ya que puede causar pérdida de precisión.

Para las comparaciones de una columna de cadena con un número, MySQL no puede usar un índice en la columna para buscar el valor rápidamente. Si str_col es una columna de cadena indexada, el índice no se puede usar al realizar la búsqueda en la siguiente declaración:

Si el campo es un tipo de carácter y tiene un índice, si la condición de la consulta se filtra por un valor numérico, entonces el SQL no podrá usar el índice en el campo.

SELECT * FROM tbl_name WHERE str_col=1;

La razón de esto es que hay muchas cadenas diferentes que pueden convertirse al valor 1, como '1', '1' o '1a'.

Probamos

mysql > create table tbl_name(id int ,str_col varchar(10),c3 varchar(5),primary key(id),key idx_str(str_col));
Query OK, 0 rows affected (0.02 sec)

mysql  > insert into tbl_name(id,str_col) values(1,'a'),(2,'b');
Query OK, 2 rows affected (0.01 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql  > insert into tbl_name(id,str_col) values(3,'3c'),(4,'4d');
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql  > desc select * from tbl_name where str_col='a';
+----+-------------+----------+------+---------------+---------+---------+-------+------+--------------------------+
| id | select_type | table    | type | possible_keys | key     | key_len | ref   | rows | Extra                    |
+----+-------------+----------+------+---------------+---------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | tbl_name | ref  | idx_str       | idx_str | 13      | const |    1 | Using where; Using index |
+----+-------------+----------+------+---------------+---------+---------+-------+------+--------------------------+

mysql  > desc select * from tbl_name where str_col=3;
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | tbl_name | ALL  | idx_str       | NULL | NULL    | NULL |    4 | Using where |
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)

mysql [localhost] {msandbox} (test) > select * from tbl_name where str_col=3;
+----+---------+------+
| id | str_col | c1   |
+----+---------+------+
|  3 | 3c      | NULL |
+----+---------+------+
1 row in set, 2 warnings (0.00 sec)

Al mismo tiempo, podemos ver que cuando comparamos la 3suma numérica str_col, él no puede usar el índice y el valor recuperado también es incorrecto.

mysql  > show warnings;
+---------+------+----------------------------------------+
| Level   | Code | Message                                |
+---------+------+----------------------------------------+
| Warning | 1292 | Truncated incorrect DOUBLE value: '3c' |
| Warning | 1292 | Truncated incorrect DOUBLE value: '4d' |
+---------+------+----------------------------------------+

MySQL para 3c y  4dse transformaron estos dos valores, en 3y4

resumen

Al realizar consultas en la base de datos, ya sea Oracle o MySQL, debe prestar atención al tipo de campo y evitar la conversión implícita, que no solo provocará consultas lentas, sino que también dará lugar a resultados incorrectos.

 

 

Supongo que te gusta

Origin blog.csdn.net/bjmsb/article/details/108501192
Recomendado
Clasificación