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

MySQL Mezcla ilegal de intercalaciones

Es útil entender las siguientes definiciones:

  • Una codificación de caracteres detalla cómo se representa cada símbolo en binario (y por lo tanto se almacena en la computadora). Por ejemplo, el símbolo é (U+00E9, letra E minúscula latina con agudo) está codificado como 0xc3a9 en UTF-8 (que MySQL llama utf8 ) y 0xe9 en Windows-1252 (que MySQL llama latin1 ).

  • Un conjunto de caracteres es el alfabeto de símbolos que se pueden representar usando una codificación de caracteres dada. De manera confusa, el término también se usa para significar lo mismo que la codificación de caracteres.

  • Una colección es una ordenación en un conjunto de caracteres, de modo que las cadenas se pueden comparar. Por ejemplo:latin1_swedish_ci de MySQL la intercalación trata la mayoría de las variaciones acentuadas de un carácter como equivalente al carácter base, mientras que su latin1_general_ci la intercalación los ordenará antes del siguiente carácter base pero no equivalente (también hay otras diferencias más significativas:como el orden de los caracteres como å , ä , ö y ß ).

MySQL decidirá qué intercalación se debe aplicar a una expresión dada como se documenta en Recopilación de expresiones :en particular, la intercalación de una columna tiene prioridad sobre la de un literal de cadena.

El WHERE cláusula de su consulta compara las siguientes cadenas:

  1. un valor en fos_user.username , codificado en el conjunto de caracteres de la columna (Windows-1252) y expresando una preferencia por su intercalación latin1_swedish_ci (con un valor de coercibilidad de 2); con

  2. el literal de cadena 'Nrv⧧Kasi' , codificado en el conjunto de caracteres de la conexión (UTF-8, según lo configurado por Doctrine) y expresando una preferencia por la intercalación de la conexión utf8_general_ci (con un valor de coercibilidad de 4).

Dado que la primera de estas cadenas tiene un valor de coercibilidad más bajo que la segunda, MySQL intenta realizar la comparación utilizando la intercalación de esa cadena:latin1_swedish_ci . Para hacerlo, MySQL intenta convertir la segunda cadena a latin1 —pero desde el carácter no existe en ese conjunto de caracteres, la comparación falla.

Advertencia

Uno debe hacer una pausa por un momento para considerar cómo está codificada actualmente la columna:está intentando filtrar registros donde fos_user.username es igual a una cadena que contiene un carácter que no puede existe en esa columna !

Si cree que la columna contiene dichos caracteres, entonces probablemente escribió en la columna mientras la codificación de caracteres de conexión estaba configurada en algo (por ejemplo, latin1 ) que hizo que MySQL interpretara la secuencia de bytes recibida como caracteres que están todos en el conjunto de caracteres de Windows-1252.

Si este es el caso, antes de continuar, ¡debería corregir sus datos!

  1. convierta dichas columnas a la codificación de caracteres que se usó en la inserción de datos, si es diferente a la codificación actual:

    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET foo;
    
  2. elimine la información de codificación asociada con tales columnas convirtiéndolas al binary juego de caracteres:

    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET binary;
    
  3. asocie con dichas columnas la codificación en la que los datos se transmitieron realmente convirtiéndolos al conjunto de caracteres relevante.

    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET bar;
    

Tenga en cuenta que, si realiza la conversión desde una codificación de varios bytes, es posible que deba aumentar el tamaño de la columna (o incluso cambiar su tipo) para acomodar la longitud máxima posible de la cadena convertida.

Una vez que uno está seguro de que las columnas están codificadas correctamente, se puede forzar la realización de la comparación mediante una intercalación Unicode mediante:

  • convirtiendo explícitamente el valor fos_user.username a un juego de caracteres Unicode:

    WHERE CONVERT(fos_user.username USING utf8) = ?
    
  • obligar al literal de cadena a tener un valor de coercibilidad más bajo que la columna (provocará una conversión implícita del valor de la columna a UTF-8):

    WHERE fos_user.username = ? COLLATE utf8_general_ci
    

O uno podría, como usted dice, convertir permanentemente la(s) columna(s) a una codificación Unicode y establecer su intercalación apropiadamente.

La consideración principal es que las codificaciones Unicode ocupan más espacio que los conjuntos de caracteres de un solo byte, por lo que:

  • es posible que se requiera más espacio de almacenamiento;

  • las comparaciones pueden ser más lentas; y

  • Es posible que sea necesario ajustar la longitud del prefijo de índice (tenga en cuenta que el máximo está en bytes, por lo que puede representar menos caracteres que antes).

Además, tenga en cuenta que, como se documenta en ALTER TABLE Sintaxis :