Solución de problemas del error "Mezcla ilegal de intercalaciones" en mysql

211

Recibo el siguiente error al intentar hacer una selección a través de un procedimiento almacenado en MySQL.

Mezcla ilegal de intercalaciones (latin1_general_cs, IMPLICIT) y (latin1_general_ci, IMPLICIT) para la operación '='

¿Alguna idea de lo que podría estar yendo mal aquí?

La clasificación de la tabla es latin1_general_ciy la de la columna en la cláusula where latin1_general_cs.

user355562
fuente
2
He estado usando una variedad de bases de datos durante largos períodos (desde 1990), y el uso de colación y coercitividad realizada por NySQL parece "loco", las bases de datos resuelven problemas que imponen el conjunto de caracteres "UNO" para la base de datos, luego depende de Los procedimientos de importación / exportación para convertir / al conjunto de caracteres único utilizado por la base de datos. Las soluciones elegidas de Mysql son perturbadoras, porque está mezclando "problemas de aplicación" (conversión de juego de caracteres) con problema de base de datos (uso de intercalación). ¿Por qué no "eliminar" esas características tontas y engorrosas de la base de datos para que sea mucho más utilizable y controlable por un
Maurizio Pievaioli

Respuestas:

216

En general, esto se debe al comparar dos cadenas de clasificación incompatibles o al intentar seleccionar datos de clasificación diferente en una columna combinada.

La cláusula le COLLATEpermite especificar la clasificación utilizada en la consulta.

Por ejemplo, la siguiente WHEREcláusula siempre dará el error que publicó:

WHERE 'A' COLLATE latin1_general_ci = 'A' COLLATE latin1_general_cs

Su solución es especificar una intercalación compartida para las dos columnas dentro de la consulta. Aquí hay un ejemplo que usa la COLLATEcláusula:

SELECT * FROM table ORDER BY key COLLATE latin1_general_ci;

Otra opción es usar el BINARYoperador:

BINARY str es la abreviatura de CAST (str AS BINARY).

Su solución podría verse más o menos así:

SELECT * FROM table WHERE BINARY a = BINARY b;

o,

SELECT * FROM table ORDER BY BINARY a;
define
fuente
2
Gracias. En realidad, parece que se está comportando bastante raro en mi caso. Cuando ejecuto la consulta tal como está, a través del navegador de consultas, me obtiene los resultados. Pero el uso de un procedimiento almacenado arroja un error.
user355562
55
Binario parecía ser la mejor solución para mí. También podría ser lo mejor para ti si no estás usando ningún filtro complicado.
Adam F
Tengo el mismo problema, la forma en que resuelvo este problema es volver a crear desde el principio. Traté de cambiar la intercalación, pero cuando me uní todavía recibí un error, así que lo intenté de esa manera. cmiiw
Bobby Z
Tenga en cuenta que hay un error en el uso de MariaDB COLLATE latin1_general_ci que causa otro error: ¡ COLLATION 'utf8_general_ci' is not valid for CHARACTER SET 'latin1''incluso si no tiene una columna con CHARACTER SET 'latin1'! La solución es usar el molde BINARIO. Ver también esta pregunta
Mel_T
154

TL; DR

Cambie la intercalación de una (o ambas) cadenas para que coincidan o agregue una COLLATEcláusula a su expresión.


  1. ¿Qué es esta cosa de "colación" de todos modos?

    Como se documenta en Conjuntos de caracteres y colaciones en general :

    Un conjunto de caracteres es un conjunto de símbolos y codificaciones. Una clasificación es un conjunto de reglas para comparar caracteres en un conjunto de caracteres. Hagamos la distinción clara con un ejemplo de un conjunto de caracteres imaginario.

    Supongamos que tenemos un alfabeto con cuatro letras: " A", " B", " a", " b". Le damos un número a cada letra: “ A” = 0, “ B” = 1, “ a” = 2, “ b” = 3. La letra “ A” es un símbolo, el número 0 es la codificación de “ A”, y la combinación de todos cuatro letras y sus codificaciones es un conjunto de caracteres .

    Supongamos que queremos comparar dos valores de cadena, " A" y " B". La forma más sencilla de hacer esto es mirar las codificaciones: 0 para " A" y 1 para " B". Como 0 es menor que 1, decimos que " A" es menor que " B". Lo que acabamos de hacer es aplicar una intercalación a nuestro conjunto de caracteres. La recopilación es un conjunto de reglas (solo una regla en este caso): "comparar las codificaciones". Llamamos a esta colación más simple de todas las colaciones binarias .

    Pero, ¿qué pasa si queremos decir que las letras minúsculas y mayúsculas son equivalentes? Entonces tendríamos al menos dos reglas: (1) tratar las letras minúsculas “ a” y “ b” como equivalentes a “ A” y “ B”; (2) luego compare las codificaciones. A esto lo llamamos insensible a mayúsculas y minúsculas colación que no . Es un poco más complejo que una colación binaria.

    En la vida real, la mayoría de los juegos de caracteres tienen muchos caracteres: no solo " A" y " B", sino alfabetos enteros, a veces múltiples alfabetos o sistemas de escritura orientales con miles de caracteres, junto con muchos símbolos especiales y signos de puntuación. También en la vida real, la mayoría de las colaciones tienen muchas reglas, no solo para distinguir entre mayúsculas y minúsculas, sino también para distinguir acentos (un "acento" es una marca adjunta a un carácter como en alemán " Ö"), y para caracteres múltiples mapeos (como la regla que " Ö" = " OE" en una de las dos colaciones alemanas).

    Se dan ejemplos adicionales en Ejemplos del efecto de la colación .

  2. Bien, pero ¿cómo decide MySQL qué intercalación usar para una expresión dada?

    Como se documenta en Colación de expresiones :

    En la gran mayoría de las declaraciones, es obvio qué clasificación utiliza MySQL para resolver una operación de comparación. Por ejemplo, en los siguientes casos, debe quedar claro que la clasificación es la clasificación de la columna charset_name:

    SELECT x FROM T ORDER BY x;
    SELECT x FROM T WHERE x = x;
    SELECT DISTINCT x FROM T;

    Sin embargo, con múltiples operandos, puede haber ambigüedad. Por ejemplo:

    SELECT x FROM T WHERE x = 'Y';

    ¿Debería la comparación utilizar la intercalación de la columna xo del literal de cadena 'Y'? Ambos xy 'Y'tienen colaciones, entonces, ¿qué colación tiene prioridad?

    El SQL estándar resuelve tales preguntas usando lo que solía llamarse reglas de "coercibilidad".

    [ deletia ]

    MySQL usa valores de coercibilidad con las siguientes reglas para resolver ambigüedades:

    • Use la clasificación con el valor de coercibilidad más bajo.

    • Si ambas partes tienen la misma coercibilidad, entonces:

      • Si ambos lados son Unicode, o ambos lados no son Unicode, es un error.

      • Si uno de los lados tiene un conjunto de caracteres Unicode y otro lado tiene un conjunto de caracteres no Unicode, el lado con el conjunto de caracteres Unicode gana, y la conversión automática del conjunto de caracteres se aplica al lado no Unicode. Por ejemplo, la siguiente declaración no devuelve un error:

        SELECT CONCAT(utf8_column, latin1_column) FROM t1;

        Devuelve un resultado que tiene un conjunto de caracteres utf8y la misma clasificación que utf8_column. Los valores de latin1_columnse convierten automáticamente utf8antes de la concatenación.

      • Para una operación con operandos de un mismo conjunto de caracteres, sino que mezclar un _bincotejo y una _cio _cscotejo, el _binse utiliza la intercalación. Esto es similar a cómo las operaciones que mezclan cadenas no binarias y binarias evalúan los operandos como cadenas binarias, excepto que es para colaciones en lugar de tipos de datos.

  3. Entonces, ¿qué es una "mezcla ilegal de colaciones"?

    Una "mezcla ilegal de colaciones" ocurre cuando una expresión compara dos cadenas de colaciones diferentes pero de igual coercibilidad y las reglas de coercibilidad no pueden ayudar a resolver el conflicto. Es la situación descrita bajo el tercer punto en la cita anterior.

    El error particular dado en la pregunta, Illegal mix of collations (latin1_general_cs,IMPLICIT) and (latin1_general_ci,IMPLICIT) for operation '='nos dice que hubo una comparación de igualdad entre dos cadenas no Unicode de igual coercibilidad. Además, nos dice que las intercalaciones no se dieron explícitamente en la declaración, sino que estaban implícitas en las fuentes de las cadenas (como los metadatos de columna).

  4. Eso está muy bien, pero ¿cómo se resuelven esos errores?

    Como sugieren los extractos manuales citados anteriormente, este problema se puede resolver de varias maneras, de las cuales dos son razonables y se recomiendan:

    • Cambie la intercalación de una (o ambas) cadenas para que coincidan y ya no haya ninguna ambigüedad.

      La forma en que esto se puede hacer depende de dónde haya venido la cadena: las expresiones literales toman la clasificación especificada en la collation_connectionvariable del sistema; los valores de las tablas toman la clasificación especificada en sus metadatos de columna.

    • Forzar una cadena para que no sea coercible.

      Omití la siguiente cita de lo anterior:

      MySQL asigna valores de coercibilidad de la siguiente manera:

      • Una COLLATEcláusula explícita tiene una coercibilidad de 0. (No coercible en absoluto).

      • La concatenación de dos cadenas con diferentes colaciones tiene una coercibilidad de 1.

      • La clasificación de una columna o un parámetro de rutina almacenado o una variable local tiene una coercibilidad de 2.

      • Una "constante del sistema" (la cadena devuelta por funciones como USER()o VERSION()) tiene una coercibilidad de 3.

      • La colación de un literal tiene una coercibilidad de 4.

      • NULLo una expresión derivada de NULLtiene una coercibilidad de 5.

      Por lo tanto, simplemente agregar una COLLATEcláusula a una de las cadenas utilizadas en la comparación forzará el uso de esa intercalación.

    Mientras que los demás serían terriblemente malas prácticas si se implementaran simplemente para resolver este error:

    • Obliga a una (o ambas) cadenas a tener algún otro valor de coercibilidad para que tenga prioridad.

      El uso de CONCAT()o CONCAT_WS()daría como resultado una cadena con una coercibilidad de 1; y (si está en una rutina almacenada) el uso de parámetros / variables locales daría como resultado cadenas con una coercibilidad de 2.

    • Cambie las codificaciones de una (o ambas) cadenas para que una sea Unicode y la otra no.

      Esto podría hacerse mediante la transcodificación con ; o cambiando el conjunto de caracteres subyacente de los datos (por ejemplo, modificando la columna, cambiando valores literales o enviándolos desde el cliente en una codificación diferente y cambiando / agregando un introductor de conjunto de caracteres). Tenga en cuenta que cambiar la codificación provocará otros problemas si algunos caracteres deseados no pueden codificarse en el nuevo conjunto de caracteres.CONVERT(expr USING transcoding_name)character_set_connectioncharacter_set_client

    • Cambie las codificaciones de una (o ambas) cadenas para que sean las mismas y cambie una cadena para usar la _binclasificación relevante .

      Los métodos para cambiar codificaciones y colaciones se han detallado anteriormente. Este enfoque sería de poca utilidad si uno realmente necesita aplicar reglas de clasificación más avanzadas que las que ofrece la _binclasificación.

eggyal
fuente
44
Tenga en cuenta que la "mezcla ilegal de colaciones" también puede surgir cuando no hay ambigüedad sobre qué colación debe usarse, pero la cadena que se debe coaccionar debe transcodificarse a una codificación en la que algunos de sus caracteres no se puedan representar. He discutido este caso en una respuesta anterior .
eggyal
55
Gran respuesta. Este debería estar más arriba, porque se sumerge en lo que los desarrolladores realmente deberían saber; no solo cómo solucionarlo, sino también comprender por qué las cosas están sucediendo de la manera en que están sucediendo.
marca el
Gracias amigo, me enseñaste algo hoy.
briankip
67

Agregar mi 2c a la discusión para futuros googlers.

Estaba investigando un problema similar en el que recibí el siguiente error al usar funciones personalizadas que recibieron un parámetro varchar:

Illegal mix of collations (utf8_unicode_ci,IMPLICIT) and 
(utf8_general_ci,IMPLICIT) for operation '='

Usando la siguiente consulta:

mysql> show variables like "collation_database";
    +--------------------+-----------------+
    | Variable_name      | Value           |
    +--------------------+-----------------+
    | collation_database | utf8_general_ci |
    +--------------------+-----------------+

Pude decir que la base de datos estaba usando utf8_general_ci , mientras que las tablas se definieron usando utf8_unicode_ci :

mysql> show table status;
    +--------------+-----------------+
    | Name         | Collation       |
    +--------------+-----------------+
    | my_view      | NULL            |
    | my_table     | utf8_unicode_ci |
    ...

Tenga en cuenta que las vistas tienen intercalación NULL . Parece que las vistas y funciones tienen definiciones de intercalación, aunque esta consulta muestra nulo para una vista. La clasificación utilizada es la clasificación de base de datos que se definió cuando se creó la vista / función.

La triste solución fue cambiar la colación db y recrear las vistas / funciones para obligarlos a usar la colación actual.

  • Cambiar la colación de la base de datos:

    ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci;
  • Cambio de la clasificación de la tabla:

    ALTER TABLE mydb CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;

Espero que esto ayude a alguien.

Ariel T
fuente
12
La clasificación también se puede establecer a nivel de columna. Puede verlo con:show full columns from my_table;
Jonathan Tran
Gracias. Acabo de soltar el esquema, lo volví a crear con la clasificación predeterminada correcta y volví a importar todo.
JRun
1
@ Jonathan Jonathan ¡Gracias! Tenía el conjunto de caracteres y la colación establecidos en todas las tablas, la base de datos y la conexión, ¡pero todavía daba un error! ¡La clasificación no se estableció en una columna! Lo arreglé conalter table <TABLE> modify column <COL> varchar(255) collate utf8_general_ci;
Chloe
2
Nota al margen para futuros googlers: incluso si su base de datos, tablas y campos tienen la misma clasificación, también debe asegurarse de que su conexión esté utilizando la misma clasificación. Todo tiene »utf8mb4_unicode_ci« pero SHOW session variables like '%collation%';te dice que »collation_connection« es »utf8mb4_general_ci«? Luego corre de SET collation_connection = utf8mb4_unicode_ciantemano.
pixelbrackets
¡Gracias! Me tomó un tiempo rastrear esto. ¡Las tablas no solo tienen que ser la misma clasificación, sino que la base de datos también lo hace!
moto
15

A veces puede ser peligroso convertir charsets, especialmente en bases de datos con grandes cantidades de datos. Creo que la mejor opción es usar el operador "binario":

e.g : WHERE binary table1.column1 = binary table2.column1
Justin Vincent
fuente
10

Tuve un problema similar, estaba tratando de usar el procedimiento FIND_IN_SET con una variable de cadena .

SET @my_var = 'string1,string2';
SELECT * from my_table WHERE FIND_IN_SET(column_name,@my_var);

y estaba recibiendo el error

Código de error: 1267. Mezcla ilegal de intercalaciones (utf8_unicode_ci, IMPLICIT) y (utf8_general_ci, IMPLICIT) para la operación 'find_in_set'

Respuesta corta:

No es necesario cambiar ninguna variable colation_YYYY, solo agregue la colación correcta al lado de su declaración de variable , es decir

SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
SELECT * from my_table WHERE FIND_IN_SET(column_name,@my_var);

Respuesta larga:

Primero verifiqué las variables de intercalación:

mysql> SHOW VARIABLES LIKE 'collation%';
    +----------------------+-----------------+
    | Variable_name        | Value           |
    +----------------------+-----------------+
    | collation_connection | utf8_general_ci |
    +----------------------+-----------------+
    | collation_database   | utf8_general_ci |
    +----------------------+-----------------+
    | collation_server     | utf8_general_ci |
    +----------------------+-----------------+

Luego revisé la clasificación de la tabla:

mysql> SHOW CREATE TABLE my_table;

CREATE TABLE `my_table` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `column_name` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=125 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

Esto significa que mi variable se configuró con la clasificación predeterminada de utf8_general_ci mientras que mi tabla se configuró como utf8_unicode_ci .

Al agregar el comando COLLATE junto a la declaración de variables, la clasificación variable coincidió con la clasificación configurada para la tabla.

nkatsar
fuente
5

Puede probar este script , que convierte todas sus bases de datos y tablas a utf8.

Mirat Can Bayrak
fuente
1
línea 24 "cur" en lugar de "cursor"
RTOSkit
2
Y triplica el tamaño de algunos índices.
Damian Yerrick
2

Solución si hay literales involucrados.

Estoy usando Pentaho Data Integration y no puedo especificar la sintaxis sql. El uso de una búsqueda de base de datos muy simple dio el error "Mezcla ilegal de intercalaciones (cp850_general_ci, COERCIBLE) y (latin1_swedish_ci, COERCIBLE) para la operación '='"

El código generado fue "SELECT DATA_DATE AS latest_DATA_DATE FROM hr_cc_normalised_data_date_v WHERE PSEUDO_KEY =?"

Para abreviar la historia, la búsqueda fue a una vista y cuando emití

mysql> show full columns from hr_cc_normalised_data_date_v;
+------------+------------+-------------------+------+-----+
| Field      | Type       | Collation         | Null | Key |
+------------+------------+-------------------+------+-----+
| PSEUDO_KEY | varchar(1) | cp850_general_ci  | NO   |     |
| DATA_DATE  | varchar(8) | latin1_general_cs | YES  |     |
+------------+------------+-------------------+------+-----+

que explica de dónde viene el 'cp850_general_ci'.

La vista se creó simplemente con 'SELECCIONAR' X ', ......' De acuerdo con los literales manuales como este, deberían heredar su conjunto de caracteres y su ordenación de la configuración del servidor que se definió correctamente como 'latin1' y 'latin1_general_cs' como este claramente no sucedió lo forcé en la creación de la vista

CREATE OR REPLACE VIEW hr_cc_normalised_data_date_v AS
SELECT convert('X' using latin1) COLLATE latin1_general_cs        AS PSEUDO_KEY
    ,  DATA_DATE
FROM HR_COSTCENTRE_NORMALISED_mV
LIMIT 1;

ahora muestra latin1_general_cs para ambas columnas y el error desapareció. :)

jc508
fuente
1

A MySQL realmente no le gusta mezclar colaciones a menos que pueda forzarlas a la misma (lo que claramente no es factible en su caso). ¿No puede forzar la misma colación para usarla mediante una cláusula COLLATE ? (o el BINARYacceso directo más simple si corresponde ...).

Alex Martelli
fuente
¿Es esto exclusivo de MySQL? ¿Cómo manejan otros sistemas una combinación de colaciones incompatibles de aparentemente igual prioridad?
eggyal
Tu enlace no es válido.
Benubird
1

Si las columnas con las que tiene problemas son "hashes", considere lo siguiente ...

Si el "hash" es una cadena binaria, realmente debería usar BINARY(...)datatype.

Si el "hash" es una cadena hexadecimal, no necesita utf8, y debe evitarlo debido a las verificaciones de caracteres, etc. Por ejemplo, MySQL MD5(...)produce una cadena hexadecimal de 32 bytes de longitud fija. SHA1(...)da una cadena hexadecimal de 40 bytes. Esto podría almacenarse en CHAR(32) CHARACTER SET ascii(o 40 para sha1).

O, mejor aún, almacenar UNHEX(MD5(...))en BINARY(16). Esto reduce a la mitad el tamaño de la columna. (Sin embargo, lo hace bastante no imprimible) SELECT HEX(hash) ...si lo desea legible.

Comparar dos BINARYcolumnas no tiene problemas de intercalación.

Rick James
fuente
1

Muy interesante ... Ahora, prepárate. Miré todas las soluciones de "agregar intercalación" y para mí, esas son correcciones de curitas. La realidad es que el diseño de la base de datos era "malo". Sí, se agregan cambios estándar y nuevas cosas, bla, bla, pero no cambia el hecho de diseño de la base de datos incorrecta. Me niego a seguir la ruta de agregar "intercalar" en todas las instrucciones SQL solo para que mi consulta funcione. La única solución que funciona para mí y prácticamente eliminará la necesidad de modificar mi código en el futuro es rediseñar la base de datos / tablas para que coincida con el conjunto de caracteres con el que viviré y adoptaré en el futuro a largo plazo. En este caso, elijo ir con el juego de caracteres " utf8mb4 ".

Entonces, la solución aquí cuando encuentre ese mensaje de error "ilegal" es rediseñar su base de datos y tablas. Es mucho más fácil y rápido de lo que parece. Puede que ni siquiera sea necesario exportar sus datos y volver a importarlos desde un CSV. Cambie el conjunto de caracteres de la base de datos y asegúrese de que todo el conjunto de caracteres de sus tablas coincida.

Usa estos comandos para guiarte:

SHOW VARIABLES LIKE "collation_database";
SHOW TABLE STATUS;

Ahora, si te gusta agregar "intercalar" aquí y allá y reforzar tu código con fuerzas que "anulan" por completo, supongo.

Nya Nguyen
fuente
0

Otra fuente del problema con las colaciones es la mysql.proctabla. Verifique las intercalaciones de sus procedimientos y funciones de almacenamiento:

SELECT
  p.db, p.db_collation, p.type, COUNT(*) cnt
FROM mysql.proc p
GROUP BY p.db, p.db_collation, p.type;

También preste atención a mysql.proc.collation_connectiony mysql.proc.character_set_clientcolumnas.

ruvim
fuente
-1

solía ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci; , pero no funcionó.

En esta consulta:

Select * from table1, table2 where table1.field = date_format(table2.field,'%H');

Este trabajo para mi:

Select * from table1, table2 where concat(table1.field) = date_format(table2.field,'%H');

Sí, solo a concat.

Knito Auron
fuente
Verifique la clasificación de sus tablas y sus columnas (muestre el estado de la tabla; y muestre las columnas completas de la tabla1;). Usar alter database no funcionaría si las tablas ya están creadas con la intercalación incorrecta.
Ariel T
ALTER DATABASE mydb DEFAULT COLLATE ... funcionó para mí, así que voté. Tal vez tenía una ventaja, ya que podía soltar y volver a crear la base de datos y cargar desde las copias de seguridad.
tobixen
-2

Este código debe colocarse dentro de Ejecutar consulta / consultas SQL en la base de datos

VENTANA DE CONSULTA SQL

ALTER TABLE `table_name` CHANGE `column_name` `column_name`   VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL;

Reemplace table_name y column_name con el nombre apropiado.

Sukumar
fuente