Risoluzione dei problemi Errore "Mix illegale di regole di confronto" in mysql


211

Ricevo l'errore di seguito quando provo a fare una selezione attraverso una procedura memorizzata in MySQL.

Mix illegale di regole di confronto (latin1_general_cs, IMPLICIT) e (latin1_general_ci, IMPLICIT) per l'operazione '='

Qualche idea su cosa potrebbe andare storto qui?

Le regole di confronto della tabella sono latin1_general_cie quella della colonna nella clausola where latin1_general_cs.


2
Sto usando una varietà di database per un lungo periodo (dal 1990), e l'uso di regole di confronto e coercibiity fatte da NySQL appare come "pazzo", i database risolvono i problemi che impongono il set di caratteri "ONE" per il database, quindi dipende da le procedure di importazione / esportazione per convertire da / al set di caratteri univoco utilizzato dal database. Le soluzioni scelte da Mysql sono dirompenti, perché mescolano "problemi applicativi" (conversione del set di caratteri) con problemi del database (utilizzo delle regole di confronto). Perché non "rimuovere" quelle sciocche e ingombranti funzionalità dal database in modo che diventi molto più utilizzabile e controllabile da un
Maurizio Pievaioli,

Risposte:


216

Ciò è generalmente causato dal confronto di due stringhe di regole di confronto incompatibili o dal tentativo di selezionare i dati di regole di confronto diverse in una colonna combinata.

La clausola COLLATEconsente di specificare le regole di confronto utilizzate nella query.

Ad esempio, la seguente WHEREclausola fornirà sempre l'errore che hai pubblicato:

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

La soluzione consiste nello specificare un confronto condiviso per le due colonne all'interno della query. Ecco un esempio che utilizza la COLLATEclausola:

SELECT * FROM table ORDER BY key COLLATE latin1_general_ci;

Un'altra opzione è quella di utilizzare l' BINARYoperatore:

BINARY str è l'abbreviazione di CAST (str AS BINARY).

La tua soluzione potrebbe assomigliare a questa:

SELECT * FROM table WHERE BINARY a = BINARY b;

o,

SELECT * FROM table ORDER BY BINARY a;

2
Grazie. In realtà sembra comportarsi in modo abbastanza strano nel mio caso. Quando eseguo la query così com'è, tramite il browser delle query, vengono recuperati i risultati. Ma l'utilizzo di una procedura memorizzata genera un errore.
user355562

5
Binary sembrava essere la soluzione migliore per me. Potrebbe essere il migliore anche per te se non stai usando filtri complicati.
Adam F

Ho lo stesso problema, il modo in cui risolvo questo problema è ricreare dall'inizio. ho provato a cambiare le regole di confronto, ma quando mi unisco ho ancora ricevuto un errore, quindi ho provato in quel modo. cmiiw
Bobby Z,

Si noti che esiste un bug in MariaDB COLLATE latin1_general_ci che causa un altro errore: COLLATION 'utf8_general_ci' is not valid for CHARACTER SET 'latin1''- anche se non si dispone di una colonna con SET DI CARATTERI 'latin1'! La soluzione è usare il cast di BINARY. Vedi anche questa domanda
Mel_T

154

TL; DR

O modifica le regole di confronto di una (o di entrambe) le stringhe in modo che corrispondano, oppure aggiungi una COLLATEclausola alla tua espressione.


  1. Che cos'è questa "raccolta"?

    Come documentato in Set di caratteri e regole di confronto in generale :

    Un set di caratteri è un set di simboli e codifiche. Una raccolta è un insieme di regole per confrontare i caratteri in un set di caratteri. Facciamo la distinzione chiara con un esempio di un set di caratteri immaginario.

    Supponiamo di avere un alfabeto con quattro lettere: “ A”, “ B”, “ a”, “ b”. Diamo a ogni lettera un numero: “ A” = 0, “ B” = 1, “ a” = 2, “ b” = 3. La lettera “ A” è un simbolo, il numero 0 è la codifica per “ A” e la combinazione di tutti quattro lettere e le loro codifiche è un set di caratteri .

    Supponiamo di voler confrontare due valori di stringa, " A" e " B". Il modo più semplice per farlo è guardare le codifiche: 0 per “ A” e 1 per “ B”. Poiché 0 è minore di 1, diciamo che " A" è inferiore a " B". Quello che abbiamo appena fatto è applicare una collation al nostro set di caratteri. Le regole di confronto sono un insieme di regole (solo una regola in questo caso): "confronta le codifiche". Chiamiamo questa semplice e semplice raccolta possibile una raccolta binaria .

    E se vogliamo dire che le lettere minuscole e maiuscole sono equivalenti? Quindi avremmo almeno due regole: (1) trattiamo le lettere minuscole " a" e " b" come equivalenti a " A" e " B"; (2) quindi confrontare le codifiche. Chiamiamo questo un confronto senza distinzione tra maiuscole e minuscole . È un po 'più complesso di un confronto binario.

    Nella vita reale, la maggior parte dei set di caratteri ha molti caratteri: non solo “ A” e “ B” ma interi alfabeti, a volte alfabeti multipli o sistemi di scrittura orientali con migliaia di caratteri, insieme a molti simboli speciali e segni di punteggiatura. Anche nella vita reale, la maggior parte delle regole di confronto ha molte regole, non solo per distinguere la lettera maiuscola, ma anche per distinguere gli accenti (un "accento" è un segno attaccato a un personaggio come in tedesco " Ö"), e per più caratteri mappature (come la regola che " Ö" = " OE" in una delle due regole di confronto tedesche).

    Ulteriori esempi sono riportati in Esempi dell'effetto della collazione .

  2. Okay, ma come fa MySQL a decidere quali regole di confronto utilizzare per una determinata espressione?

    Come documentato in Collation of Expressions :

    Nella stragrande maggioranza delle affermazioni, è ovvio che regole di confronto utilizzate da MySQL per risolvere un'operazione di confronto. Ad esempio, nei seguenti casi, dovrebbe essere chiaro che le regole di confronto sono le regole di confronto della colonna charset_name:

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

    Tuttavia, con più operandi, può esserci ambiguità. Per esempio:

    SELECT x FROM T WHERE x = 'Y';

    Il confronto dovrebbe usare le regole di confronto della colonna xo della stringa letterale 'Y'? Entrambi xe 'Y'hanno regole di confronto, quindi quale confronto ha la precedenza?

    SQL standard risolve tali domande utilizzando quelle che un tempo venivano chiamate regole di "coercibilità".

    [ deletia ]

    MySQL utilizza i valori di coercibilità con le seguenti regole per risolvere le ambiguità:

    • Utilizzare le regole di confronto con il valore di coercibilità più basso.

    • Se entrambe le parti hanno la stessa coercibilità, allora:

      • Se entrambi i lati sono Unicode o entrambi non sono Unicode, si tratta di un errore.

      • Se uno dei lati ha un set di caratteri Unicode e un altro lato ha un set di caratteri non Unicode, il lato con il set di caratteri Unicode vince e la conversione automatica del set di caratteri viene applicata al lato non Unicode. Ad esempio, la seguente istruzione non restituisce un errore:

        SELECT CONCAT(utf8_column, latin1_column) FROM t1;

        Restituisce un risultato che ha un set di caratteri utf8e la stessa fascicolazione di utf8_column. I valori di latin1_columnvengono convertiti automaticamente in utf8prima della concatenazione.

      • Per un'operazione con operandi dallo stesso insieme di caratteri ma che mescolare una _bincollazione e una _cio _csfascicolazione, la _binsi utilizza collazione. Questo è simile al modo in cui le operazioni che mescolano stringhe non binarie e binarie valutano gli operandi come stringhe binarie, tranne per il fatto che è per regole di confronto anziché tipi di dati.

  3. Quindi cos'è un "mix illegale di regole di confronto"?

    Un "mix illegale di regole di confronto" si verifica quando un'espressione confronta due stringhe di regole di confronto diverse ma di uguale coercibilità e le regole di coercibilità non possono aiutare a risolvere il conflitto. È la situazione descritta al terzo punto elenco nella citazione precedente.

    Il particolare errore riportato nella domanda, Illegal mix of collations (latin1_general_cs,IMPLICIT) and (latin1_general_ci,IMPLICIT) for operation '='ci dice che c'era un confronto di uguaglianza tra due stringhe non Unicode di uguale coercibilità. Inoltre ci dice che le regole di confronto non sono state fornite esplicitamente nella dichiarazione ma piuttosto sono state implicite dalle fonti delle stringhe (come i metadati di colonna).

  4. Va benissimo, ma come si risolvono tali errori?

    Come suggeriscono gli estratti manuali sopra citati, questo problema può essere risolto in diversi modi, di cui due sono sensibili e raccomandabili:

    • Modifica le regole di confronto di una (o di entrambe) le stringhe in modo che corrispondano e non vi siano più ambiguità.

      Il modo in cui ciò può essere fatto dipende da dove proviene la stringa: le espressioni letterali prendono le regole di confronto specificate nella collation_connectionvariabile di sistema; i valori delle tabelle accettano le regole di confronto specificate nei metadati della colonna.

    • Forza una stringa per non essere coercibile.

      Ho omesso la seguente citazione da quanto sopra:

      MySQL assegna i valori di coercibilità come segue:

      • Una COLLATEclausola esplicita ha una coercibilità di 0. (Non coercibile affatto.)

      • La concatenazione di due stringhe con regole di confronto diverse ha una coercibilità di 1.

      • Le regole di confronto di una colonna o di un parametro di routine memorizzato o di una variabile locale hanno una coercibilità di 2.

      • Una "costante di sistema" (la stringa restituita da funzioni come USER()o VERSION()) ha una coercibilità di 3.

      • La raccolta di un letterale ha una coercibilità di 4.

      • NULLo un'espressione da cui deriva NULLha una coercibilità di 5.

      Pertanto, semplicemente l'aggiunta di una COLLATEclausola a una delle stringhe utilizzate nel confronto forzerà l'uso di tale confronto.

    Mentre gli altri sarebbero terribilmente cattive pratiche se fossero schierati semplicemente per risolvere questo errore:

    • Forza una (o entrambe) le stringhe ad avere un altro valore di coercibilità in modo da avere la precedenza.

      L'uso di CONCAT()o CONCAT_WS()comporterebbe una stringa con una coercibilità di 1; e (se in una routine memorizzata) l'uso di parametri / variabili locali comporterebbe stringhe con una coercibilità di 2.

    • Modifica le codifiche di una (o di entrambe) le stringhe in modo che una sia Unicode e l'altra no.

      Questo potrebbe essere fatto tramite la transcodifica con ; o modificando il set di caratteri sottostante dei dati (ad esempio modificando la colonna, cambiando per valori letterali o inviandoli dal client in una codifica diversa e cambiando / aggiungendo un introduttore di set di caratteri). La modifica della codifica comporterà altri problemi se alcuni caratteri desiderati non possono essere codificati nel nuovo set di caratteri.CONVERT(expr USING transcoding_name)character_set_connectioncharacter_set_client

    • Modificare le codifiche di una (o di entrambe) le stringhe in modo che siano entrambe uguali e modificare una stringa per utilizzare le _binregole di confronto pertinenti .

      I metodi per modificare codifiche e regole di confronto sono stati descritti in dettaglio sopra. Questo approccio sarebbe di scarsa utilità se si dovesse effettivamente applicare regole di confronto più avanzate rispetto a quelle offerte dal _binconfronto.


4
Si noti che il "mix illegale di regole di confronto" può anche verificarsi quando non vi è alcuna ambiguità su quale modalità di confronto debba essere utilizzata, ma la stringa che deve essere forzata deve essere transcodificata in una codifica in cui alcuni dei suoi caratteri non possono essere rappresentati. Ho discusso di questo caso in una risposta precedente .
Eggyal

5
Bella risposta. Questo dovrebbe essere il più in alto, perché si tuffa in ciò che gli sviluppatori dovrebbero davvero sapere; non solo come risolverlo, ma capire davvero perché le cose stanno accadendo nel modo in cui stanno accadendo.
segna il

Grazie amico, mi hai insegnato qualcosa oggi.
briankip,

67

Aggiungendo il mio 2c alla discussione per futuri googler.

Stavo esaminando un problema simile in cui ho riscontrato il seguente errore durante l'utilizzo di funzioni personalizzate che hanno ricevuto un parametro varchar:

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

Utilizzando la seguente query:

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

Sono stato in grado di dire che il DB stava usando utf8_general_ci , mentre le tabelle sono state definite usando utf8_unicode_ci :

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

Si noti che le viste hanno regole di confronto NULL . Sembra che le viste e le funzioni abbiano definizioni di confronto anche se questa query mostra null per una vista. Le regole di confronto utilizzate sono le regole di confronto DB definite al momento della creazione della vista / funzione.

La triste soluzione era sia quella di cambiare le regole di confronto db e di ricreare le viste / funzioni per costringerle a utilizzare le regole di confronto correnti.

  • Modifica delle regole di confronto del db:

    ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci;
  • Modifica delle regole di confronto della tabella:

    ALTER TABLE mydb CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;

Spero che questo possa aiutare qualcuno.


12
Le regole di confronto possono anche essere impostate a livello di colonna. Puoi vederlo con:show full columns from my_table;
Jonathan Tran,

Grazie. Ho appena abbandonato lo schema e lo ho ricreato con le regole di confronto predefinite corrette e ho reimportato tutto.
JRun

1
@JonathanTran Grazie! Avevo il set di caratteri e il set di regole di confronto su tutte le tabelle, il database e la connessione, ma continuava a dare un errore! La raccolta non è stata impostata su una colonna! L'ho risolto conalter table <TABLE> modify column <COL> varchar(255) collate utf8_general_ci;
Chloe

2
Sidenote per futuri googler: anche se il tuo database, le tabelle e i campi hanno tutti le stesse regole di confronto, devi anche assicurarti che la tua connessione stia utilizzando le stesse regole di confronto. Tutto ha »utf8mb4_unicode_ci« ma SHOW session variables like '%collation%';ti dice che »collation_connection« è »utf8mb4_general_ci«? Quindi eseguire in SET collation_connection = utf8mb4_unicode_cianticipo.
pixelbrackets

Grazie! Mi ci è voluto un po 'per rintracciarlo. Non solo le tabelle devono essere le stesse regole di confronto, ma anche il DB!
moto

15

A volte può essere pericoloso convertire i set di caratteri, specialmente su database con enormi quantità di dati. Penso che l'opzione migliore sia usare l'operatore "binario":

e.g : WHERE binary table1.column1 = binary table2.column1

10

Ho avuto un problema simile, stavo cercando di utilizzare la procedura FIND_IN_SET con una variabile stringa .

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

e stava ricevendo l'errore

Codice errore: 1267. Mix illegale di regole di confronto (utf8_unicode_ci, IMPLICIT) e (utf8_general_ci, IMPLICIT) per l'operazione 'find_in_set'

Risposta breve:

Non è necessario modificare alcuna variabile collation_YYYY, è sufficiente aggiungere la fascicolazione corretta accanto alla dichiarazione della variabile , ad es

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

Risposta lunga:

Ho prima verificato le variabili di confronto:

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

Quindi ho controllato le regole di confronto della tabella:

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;

Ciò significa che la mia variabile è stata configurata con le regole di confronto predefinite di utf8_general_ci mentre la mia tabella è stata configurata come utf8_unicode_ci .

Aggiungendo il comando COLLATE accanto alla dichiarazione delle variabili, le regole di confronto variabili corrispondevano alle regole di confronto configurate per la tabella.



2

Soluzione se sono coinvolti letterali.

Sto usando Pentaho Data Integration e non riesco a specificare la sintassi sql. L'utilizzo di una ricerca DB molto semplice ha dato l'errore "Mix illegale di regole di confronto (cp850_general_ci, COERCIBLE) e (latin1_swedish_ci, COERCIBLE) per l'operazione '='"

Il codice generato è stato "SELECT DATA_DATE AS latest_DATA_DATE FROM hr_cc_normalised_data_date_v WHERE PSEUDO_KEY =?"

In breve, la ricerca è stata una visione e quando ho pubblicato

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  |     |
+------------+------------+-------------------+------+-----+

che spiega da dove proviene il 'cp850_general_ci'.

La vista è stata semplicemente creata con 'SELECT' X ', ......' Secondo i manuali letterali come questo dovrebbero ereditare il loro set di caratteri e le regole di confronto dalle impostazioni del server che sono state correttamente definite come 'latin1' e 'latin1_general_cs' come questo chiaramente non è successo, l'ho forzato nella creazione della 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;

ora mostra latin1_general_cs per entrambe le colonne e l'errore è scomparso. :)


1

A MySQL non piace davvero mescolare le collazioni a meno che non sia in grado di costringerle allo stesso (che chiaramente non è fattibile nel tuo caso). Non puoi semplicemente forzare la stessa raccolta da utilizzare tramite una clausola COLLATE ? (o il BINARYcollegamento più semplice, se applicabile ...).


Questo è unico per MySQL? In che modo altri sistemi gestiscono un mix di regole di confronto incompatibili con una priorità apparentemente uguale?
Eggyal

Il tuo link non è valido
Benubird,

1

Se le colonne con cui hai problemi sono "hash", considera quanto segue ...

Se "hash" è una stringa binaria, dovresti davvero usare il BINARY(...)tipo di dati.

Se "hash" è una stringa esadecimale, non hai bisogno di utf8 e dovresti evitarlo a causa di controlli dei caratteri, ecc. Ad esempio, MySQL MD5(...)produce una stringa esadecimale a 32 byte di lunghezza fissa. SHA1(...)fornisce una stringa esadecimale di 40 byte. Questo potrebbe essere memorizzato in CHAR(32) CHARACTER SET ascii(o 40 per sha1).

O, meglio ancora, conservare UNHEX(MD5(...))in BINARY(16). Questo riduce della metà la dimensione della colonna. (Tuttavia, lo rende piuttosto non stampabile.) SELECT HEX(hash) ...Se lo si desidera leggere.

Il confronto tra due BINARYcolonne non ha problemi di confronto.


1

Molto interessante ... Ora, sii pronto. Ho esaminato tutte le soluzioni "aggiungi fascicolazione" e per me quelle sono correzioni di aiuti di banda. La realtà è che il design del database era "cattivo". Sì, vengono apportate modifiche standard e nuove cose, blah blah, ma non cambia il fatto negativo di progettazione del database. Mi rifiuto di seguire il percorso di aggiunta di "fascicolazione" in tutte le istruzioni SQL solo per far funzionare la mia query. L'unica soluzione che funziona per me ed eliminerà virtualmente la necessità di modificare il mio codice in futuro è riprogettare il database / le tabelle in modo che corrispondano al set di caratteri con cui vivrò e che abbraccerò per il futuro a lungo termine. In questo caso, ho scelto di utilizzare il set di caratteri " utf8mb4 ".

Quindi la soluzione qui quando si incontra quel messaggio di errore "illegale" è riprogettare il database e le tabelle. È molto più facile e veloce di quanto sembri. Potrebbe non essere nemmeno necessario esportare i tuoi dati e reimportarli da un CSV. Modificare il set di caratteri del database e assicurarsi che tutto il set di caratteri delle tabelle corrisponda.

Usa questi comandi per guidarti:

SHOW VARIABLES LIKE "collation_database";
SHOW TABLE STATUS;

Ora, se ti piace aggiungere "collate" qua e là e rinforzare il tuo codice con forze "override" complete, sii la mia ipotesi.



0

Un'altra fonte del problema con le regole di confronto è la mysql.proctabella. Controlla le regole di confronto delle procedure e delle funzioni di archiviazione:

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

Anche prestare attenzione alla mysql.proc.collation_connectione mysql.proc.character_set_clientcolonne.



-1

Ho usato ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci;, ma non ha funzionato.

In questa query:

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

Questo lavoro per me:

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

Sì, solo a concat.


Controlla le regole di confronto tra le tue tabelle e le loro colonne (mostra lo stato della tabella e mostra le colonne complete dalla tabella 1;). L'uso di alter database non funzionerebbe se le tabelle sono già state create con regole di confronto errate.
Ariel T,

ALTER DATABASE mydb DEFAULT COLLATE ... ha funzionato per me, quindi voto positivo. Forse ho avuto un vantaggio dal momento che ho potuto rilasciare e ricreare il database e caricare dai backup.
Tobixen,

-2

Questo codice deve essere inserito in Esegui query / query SQL nel database

SQL QUERY WINDOW

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

Sostituire table_name e column_name con il nome appropriato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.