Solução de problemas do erro “Mix ilegal de agrupamentos” no mysql

210

Estou recebendo o erro abaixo ao tentar fazer uma seleção através de um procedimento armazenado no MySQL.

Mistura ilegal de agrupamentos (latin1_general_cs, IMPLICIT) e (latin1_general_ci, IMPLICIT) para a operação '='

Alguma idéia do que pode estar errado aqui?

O agrupamento da tabela é latin1_general_cie o da coluna na cláusula where latin1_general_cs.

user355562
fonte
2
Eu uso vários tipos de bancos de dados por um longo período (desde 1990), e o uso de agrupamento e coercibiidade feitos pelo NySQL aparece como "louco", os bancos de dados resolvem problemas que impõem o "ONE" conjunto de caracteres para o banco de dados e, em seguida, depende de os procedimentos de importação / exportação para converter de / para o conjunto de caracteres exclusivo usado pelo banco de dados. As soluções escolhidas do Mysql são perturbadoras, porque misturam "problemas de aplicativos" (conversão de conjunto de caracteres) com problemas de banco de dados (uso de agrupamento). Por que não "remover" que características tolas e pesados do banco de dados para que ele se tornar muito mais útil e controlável por um
Maurizio Pievaioli

Respostas:

216

Isso geralmente é causado pela comparação de duas cadeias de agrupamento incompatível ou pela tentativa de selecionar dados de agrupamento diferente em uma coluna combinada.

A cláusula COLLATEpermite especificar o agrupamento usado na consulta.

Por exemplo, a seguinte WHEREcláusula sempre fornecerá o erro que você postou:

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

Sua solução é especificar um agrupamento compartilhado para as duas colunas na consulta. Aqui está um exemplo que usa a COLLATEcláusula:

SELECT * FROM table ORDER BY key COLLATE latin1_general_ci;

Outra opção é usar o BINARYoperador:

BINARY str é a abreviação de CAST (str AS BINARY).

Sua solução pode ser algo como isto:

SELECT * FROM table WHERE BINARY a = BINARY b;

ou,

SELECT * FROM table ORDER BY BINARY a;
define
fonte
2
Obrigado. Na verdade, parece estar se comportando muito estranho no meu caso. Quando executo a consulta como está, no navegador de consulta, ela obtém os resultados. Mas o uso de um procedimento armazenado gera um erro.
user355562
5
Binário parecia ser a melhor solução para mim. Pode ser o melhor para você também, se você não estiver usando nenhum filtro complicado.
Adam F
Eu tenho o mesmo problema, a maneira como resolvo esse problema é recriada desde o início. Eu tentei alterar o agrupamento, mas quando me associo ainda tem um erro, então tentei dessa maneira. cmiiw
Bobby Z /
Observe que existe um erro no MariaDB COLLATE latin1_general_ci que causa outro erro: COLLATION 'utf8_general_ci' is not valid for CHARACTER SET 'latin1''- mesmo se você não tiver uma coluna com CHARACTER SET 'latin1'! A solução é usar o elenco BINARY. Veja também esta pergunta
Mel_T 22/10/19
154

TL; DR

Altere o agrupamento de uma (ou ambas) das seqüências para que correspondam ou adicione uma COLLATEcláusula à sua expressão.


  1. O que é esse material de "agrupamento", afinal?

    Conforme documentado em Conjuntos de caracteres e agrupamentos em geral :

    Um conjunto de caracteres é um conjunto de símbolos e codificações. Um agrupamento é um conjunto de regras para comparar caracteres em um conjunto de caracteres. Vamos deixar clara a distinção com um exemplo de conjunto de caracteres imaginários.

    Suponha que tenhamos um alfabeto com quatro letras: “ A”, “ B”, “ a”, “ b”. Atribuímos a cada letra um número: “ A” = 0, “ B” = 1, “ a” = 2, “ b” = 3. A letra “ A” é um símbolo, o número 0 é a codificação para “ A” e a combinação de todos quatro letras e suas codificações é um conjunto de caracteres .

    Suponha que desejamos comparar dois valores de string, “ A” e “ B”. A maneira mais simples de fazer isso é olhar para as codificações: 0 para " A" e 1 para " B". Como 0 é menor que 1, dizemos que " A" é menor que " B". O que acabamos de fazer é aplicar um agrupamento ao nosso conjunto de caracteres. O agrupamento é um conjunto de regras (apenas uma regra neste caso): "compare as codificações". Chamamos isso de mais simples de todas as agrupações possíveis, uma ordenação binária .

    Mas e se quisermos dizer que as letras minúsculas e maiúsculas são equivalentes? Então teríamos pelo menos duas regras: (1) trate as letras minúsculas “ a” e “ b” como equivalentes a “ A” e “ B”; (2) depois compare as codificações. Chamamos isso de agrupamento que não diferencia maiúsculas de minúsculas . É um pouco mais complexo que um agrupamento binário.

    Na vida real, a maioria dos conjuntos de caracteres possui muitos caracteres: não apenas “ A” e “ B”, mas alfabetos inteiros, às vezes vários alfabetos ou sistemas de escrita oriental com milhares de caracteres, juntamente com muitos símbolos especiais e sinais de pontuação. Também na vida real, a maioria dos agrupamentos tem muitas regras, não apenas para distinguir letras de papel, mas também para distinguir acentos (um "acento" é uma marca anexada a um caractere como no alemão " Ö") e para caracteres múltiplos mapeamentos (como a regra que “ Ö” = “ OE” em um dos dois agrupamentos alemães).

    Outros exemplos são dados em Exemplos do efeito de agrupamento .

  2. Ok, mas como o MySQL decide qual agrupamento usar para uma determinada expressão?

    Conforme documentado em Agrupamento de expressões :

    Na grande maioria das declarações, é óbvio que agrupamento o MySQL usa para resolver uma operação de comparação. Por exemplo, nos seguintes casos, deve ficar claro que o agrupamento é o agrupamento da coluna charset_name:

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

    No entanto, com vários operandos, pode haver ambiguidade. Por exemplo:

    SELECT x FROM T WHERE x = 'Y';

    A comparação deve usar o agrupamento da coluna xou da string literal 'Y'? Ambos xe 'Y'têm agrupamentos, então qual agrupamento tem precedência?

    O SQL padrão resolve essas questões usando o que costumava ser chamado de regras de "coercibilidade".

    [ deletia ]

    O MySQL usa valores de coercibilidade com as seguintes regras para resolver ambiguidades:

    • Use o agrupamento com o menor valor de coercibilidade.

    • Se ambos os lados tiverem a mesma coerção, então:

      • Se os dois lados são Unicode ou ambos não são Unicode, é um erro.

      • Se um dos lados tiver um conjunto de caracteres Unicode e outro lado tiver um conjunto de caracteres não Unicode, o lado com o conjunto de caracteres Unicode vencerá e a conversão automática do conjunto de caracteres será aplicada ao lado não Unicode. Por exemplo, a seguinte instrução não retorna um erro:

        SELECT CONCAT(utf8_column, latin1_column) FROM t1;

        Retorna um resultado que possui um conjunto de caracteres utf8e o mesmo agrupamento que utf8_column. Os valores de latin1_columnsão convertidos automaticamente para utf8antes da concatenação.

      • Para uma operação com operandos do mesmo conjunto de caracteres, mas que mistura um _binagrupamento e um _ciou _csagrupamento, o _binagrupamento é usado. É semelhante à maneira como as operações que combinam seqüências não binárias e binárias avaliam os operandos como sequências binárias, exceto que são para agrupamentos e não para tipos de dados.

  3. Então, o que é uma "mistura ilegal de agrupamentos"?

    Uma "mistura ilegal de agrupamentos" ocorre quando uma expressão compara duas cadeias de agrupamentos diferentes, mas de igual coerência e as regras de coerção não podem ajudar a resolver o conflito. É a situação descrita no terceiro ponto da citação acima.

    O erro específico fornecido na pergunta, Illegal mix of collations (latin1_general_cs,IMPLICIT) and (latin1_general_ci,IMPLICIT) for operation '='diz-nos que houve uma comparação de igualdade entre duas seqüências não-Unicode de igual coercibilidade. Além disso, nos diz que os agrupamentos não foram fornecidos explicitamente na declaração, mas foram implícitos nas fontes das strings (como metadados da coluna).

  4. Tudo muito bem, mas como se resolve esses erros?

    Como sugerem os extratos do manual citados acima, esse problema pode ser resolvido de várias maneiras, das quais duas são sensíveis e recomendadas:

    • Altere o agrupamento de uma (ou de ambas) das seqüências de caracteres para que correspondam e não exista mais ambiguidade.

      Como isso pode ser feito depende de onde a string veio: Expressões literais recebem o agrupamento especificado na collation_connectionvariável do sistema; os valores das tabelas recebem o agrupamento especificado em seus metadados da coluna.

    • Force uma sequência a não ser coercível.

      Omiti a seguinte citação do acima exposto:

      O MySQL atribui valores de coercibilidade da seguinte forma:

      • Uma COLLATEcláusula explícita tem uma coercibilidade de 0. (Não é coercível.)

      • A concatenação de duas seqüências com diferentes agrupamentos tem uma coercibilidade de 1.

      • O agrupamento de uma coluna ou um parâmetro de rotina armazenado ou variável local tem uma coercibilidade de 2.

      • Uma "constante do sistema" (a sequência retornada por funções como USER()ou VERSION()) tem uma coercibilidade de 3.

      • O agrupamento de um literal tem uma coercibilidade de 4.

      • NULLou uma expressão derivada NULLtem uma coercibilidade de 5.

      Assim, simplesmente adicionar uma COLLATEcláusula a uma das strings usadas na comparação forçará o uso desse agrupamento.

    Enquanto os outros seriam uma péssima prática se eles fossem implantados apenas para resolver este erro:

    • Forçar uma (ou ambas) das seqüências de caracteres a ter algum outro valor de coercibilidade, para que uma tenha precedência.

      O uso de CONCAT()ou CONCAT_WS()resultaria em uma string com uma coercibilidade de 1; e (se em uma rotina armazenada) o uso de parâmetros / variáveis ​​locais resultaria em seqüências de caracteres com uma coercibilidade de 2.

    • Altere as codificações de uma (ou de ambas) das seqüências de caracteres para que uma seja Unicode e a outra não.

      Isso pode ser feito via transcodificação com ; ou via alteração do conjunto de caracteres subjacente dos dados (por exemplo, modificação da coluna, alteração de valores literais ou envio do cliente em uma codificação diferente e alteração / adição de um introdutor de conjunto de caracteres). Observe que a alteração da codificação levará a outros problemas se alguns caracteres desejados não puderem ser codificados no novo conjunto de caracteres.CONVERT(expr USING transcoding_name)character_set_connectioncharacter_set_client

    • Altere as codificações de uma (ou ambas) das seqüências de caracteres para que elas sejam as mesmas e altere uma sequência para usar o _binagrupamento relevante .

      Os métodos para alterar codificações e agrupamentos foram detalhados acima. Essa abordagem seria de pouca utilidade se alguém realmente precisasse aplicar regras de agrupamento mais avançadas do que as oferecidas pelo _binagrupamento.

eggyal
fonte
4
Observe que a "mistura ilegal de agrupamentos" também pode surgir quando não há ambiguidade sobre qual agrupamento deve ser usado, mas a sequência a ser coagida deve ser transcodificada para uma codificação na qual alguns de seus caracteres não podem ser representados. Eu discuti esse caso em uma resposta anterior .
eggyal 11/01
5
Ótima resposta. Este deve ser o mais adiante, porque mergulha no que os desenvolvedores realmente devem saber; não apenas como consertá-lo, mas realmente entender por que as coisas estão acontecendo do jeito que estão;
mar
Obrigado cara, você me ensinou algo hoje.
Briankip
66

Adicionando meu 2c à discussão para futuros googlers.

Eu estava investigando um problema semelhante no qual obtive o seguinte erro ao usar funções personalizadas que receberam um parâmetro varchar:

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

Usando a seguinte consulta:

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

Consegui dizer que o banco de dados estava usando utf8_general_ci , enquanto as tabelas foram definidas usando utf8_unicode_ci :

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

Observe que as visualizações têm agrupamento NULL . Parece que as visualizações e funções têm definições de agrupamento, embora essa consulta mostre nulo para uma visualização. O agrupamento usado é o agrupamento de banco de dados que foi definido quando a exibição / função foi criada.

A solução triste foi alterar o agrupamento de banco de dados e recriar as visualizações / funções para forçá-los a usar o agrupamento atual.

  • Alterando o agrupamento do banco de dados:

    ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci;
  • Alterando o agrupamento da tabela:

    ALTER TABLE mydb CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;

Espero que isso ajude alguém.

Ariel T
fonte
12
O agrupamento também pode ser definido no nível da coluna. Você pode vê-lo com:show full columns from my_table;
Jonathan Tran
Obrigado. Acabei de soltar o esquema e o recriei com o agrupamento padrão correto e reimportei tudo.
21413 JRun
1
@JonathanTran Thank you! Eu tinha o conjunto de caracteres e o agrupamento em todas as tabelas, banco de dados e conexão, mas ainda estava dando um erro! O agrupamento não foi definido em uma coluna! Eu consertei comalter table <TABLE> modify column <COL> varchar(255) collate utf8_general_ci;
Chloe
2
Sidenote para futuros googlers: mesmo que seu banco de dados, tabelas e campos tenham todos o mesmo agrupamento, você também deve garantir que sua conexão esteja usando o mesmo agrupamento. Tudo tem »utf8mb4_unicode_ci« mas SHOW session variables like '%collation%';diz que »collation_connection« é »utf8mb4_general_ci«? Então corra de SET collation_connection = utf8mb4_unicode_ciantemão.
pixelbrackets
Obrigado! Levei um tempo para descobrir isso. Não apenas as tabelas precisam ter o mesmo agrupamento, mas o banco de dados também!
moto
15

Às vezes, pode ser perigoso converter conjuntos de caracteres, especialmente em bancos de dados com grandes quantidades de dados. Eu acho que a melhor opção é usar o operador "binário":

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

Eu tive um problema semelhante, estava tentando usar o procedimento FIND_IN_SET com uma variável de string .

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

e estava recebendo o erro

Código de erro: 1267. Combinação ilegal de agrupamentos (utf8_unicode_ci, IMPLICIT) e (utf8_general_ci, IMPLICIT) para a operação 'find_in_set'

Resposta curta:

Não é necessário alterar nenhuma variável collation_YYYY; basta adicionar o agrupamento correto ao lado da sua declaração de variável , ou seja,

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

Resposta longa:

Primeiro verifiquei as variáveis ​​de agrupamento:

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

Depois verifiquei o agrupamento da tabela:

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;

Isso significa que minha variável foi configurada com o agrupamento padrão de utf8_general_ci enquanto minha tabela foi configurada como utf8_unicode_ci .

Ao adicionar o comando COLLATE ao lado da declaração da variável, o agrupamento da variável correspondeu ao agrupamento configurado para a tabela.

nkatsar
fonte
5

Você pode tentar este script , que converte todos os seus bancos de dados e tabelas em utf8.

Mirat Can Bayrak
fonte
1
linha 24 "cur" em vez de "cursor"
RTOSkit
2
E triplica o tamanho de alguns índices.
precisa saber é o seguinte
2

Solução se literais estiverem envolvidos.

Estou usando o Pentaho Data Integration e não consigo especificar a sintaxe sql. O uso de uma pesquisa de banco de dados muito simples deu o erro "Mistura ilegal de agrupamentos (cp850_general_ci, COERCIBLE) e (latin1_swedish_ci, COERCIBLE) para a operação '='"

O código gerado foi "SELECT DATA_DATE AS latest_DATA_DATE FROM hr_cc_normalised_data_date_v WHERE PSEUDO_KEY =?"

Para resumir a história, a pesquisa foi vista e quando eu publiquei

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 onde vem o 'cp850_general_ci'.

A visualização foi simplesmente criada com 'SELECT' X ', ......' De acordo com o manual, literais como este devem herdar seu conjunto de caracteres e agrupamento das configurações do servidor definidas corretamente como 'latin1' e 'latin1_general_cs' como este claramente não aconteceu eu forcei na criação da visão

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;

agora mostra latin1_general_cs para ambas as colunas e o erro desapareceu. :)

jc508
fonte
1

O MySQL realmente não gosta de misturar agrupamentos, a menos que possa coagi-los ao mesmo (o que claramente não é viável no seu caso). Você não pode simplesmente forçar o mesmo agrupamento a ser usado por meio de uma cláusula COLLATE ? (ou o BINARYatalho mais simples, se aplicável ...).

Alex Martelli
fonte
Isso é exclusivo do MySQL? Como outros sistemas lidam com uma combinação de agrupamentos incompatíveis de prioridade aparentemente igual?
eggyal 15/01
O seu link não é válido.
precisa saber é o seguinte
1

Se as colunas com as quais você está tendo problemas são "hashes", considere o seguinte ...

Se o "hash" for uma string binária, você realmente deve usar o BINARY(...)tipo de dados.

Se o "hash" for uma sequência hexadecimal, você não precisa de utf8 e deve evitá-lo devido a verificações de caracteres etc. Por exemplo, o MySQL MD5(...)produz uma sequência hexadecimal de 32 bytes de comprimento fixo. SHA1(...)fornece uma cadeia hexadecimal de 40 bytes. Isso pode ser armazenado em CHAR(32) CHARACTER SET ascii(ou 40 para sha1).

Ou, ainda melhor, armazenar UNHEX(MD5(...))em BINARY(16). Isso corta pela metade o tamanho da coluna. (No entanto, torna-o bastante imprimível.) SELECT HEX(hash) ... Se você quiser que seja legível.

A comparação de duas BINARYcolunas não apresenta problemas de agrupamento.

Rick James
fonte
1

Muito interessante ... Agora, esteja pronto. Eu olhei para todas as soluções "adicionar agrupar" e para mim, essas são correções de band-aid. A realidade é que o design do banco de dados era "ruim". Sim, alterações padrão e coisas novas são adicionadas, blá, blá, mas isso não altera o fato ruim de design do banco de dados. Recuso-me a seguir a rota de adicionar "agrupar" em todas as instruções SQL apenas para fazer minha consulta funcionar. A única solução que funciona para mim e praticamente elimina a necessidade de ajustar meu código no futuro é redesenhar o banco de dados / tabelas para corresponder ao conjunto de caracteres com o qual viverei e adotarei no futuro a longo prazo. Nesse caso, optei por usar o conjunto de caracteres " utf8mb4 ".

Portanto, a solução aqui quando você encontrar essa mensagem de erro "ilegal" é redesenhar seu banco de dados e tabelas. É muito mais fácil e rápido do que parece. Exportar seus dados e reimportá-los de um CSV pode até não ser necessário. Altere o conjunto de caracteres do banco de dados e verifique se todo o conjunto de caracteres de suas tabelas corresponde.

Use estes comandos para guiá-lo:

SHOW VARIABLES LIKE "collation_database";
SHOW TABLE STATUS;

Agora, se você gosta de adicionar "agrupar" aqui e ali e aprimorar seu código com "substituições" de forças, seja meu palpite.

Nya Nguyen
fonte
0

Outra fonte do problema de agrupamentos é a mysql.proctabela. Verifique agrupamentos de seus procedimentos e funções de armazenamento:

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

Também preste atenção mysql.proc.collation_connectione mysql.proc.character_set_clientcolunas.

ruvim
fonte
-1

eu usei ALTER DATABASE mydb DEFAULT COLLATE utf8_unicode_ci; , mas não funcionou.

Nesta consulta:

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

Este trabalho para mim:

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

Sim, apenas a concat.

Knito Auron
fonte
Verifique o agrupamento de suas tabelas e suas colunas (mostre o status da tabela; e mostre colunas completas da tabela1;). O uso de alter database não funcionaria se as tabelas já fossem criadas com o agrupamento errado.
Ariel T
ALTER DATABASE mydb DEFAULT COLLATE ... funcionou para mim, de forma positiva. Talvez eu tenha uma vantagem, já que eu poderia descartar e recriar o banco de dados e carregar de backups.
Tobixen
-2

Esse código precisa ser colocado dentro de Executar consultas / consultas SQL no banco de dados

JANELA DE CONSULTA SQL

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

Substitua table_name e column_name pelo nome apropriado.

Sukumar
fonte