Combinação ilegal de agrupamentos (utf8_unicode_ci, IMPLICIT) e (utf8_general_ci, IMPLICIT) para a operação '='

160

Mensagem de erro no MySql:

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

Passei por várias outras postagens e não consegui resolver esse problema. A parte afetada é algo semelhante a isto:

CREATE TABLE users (
    userID INT UNSIGNED NOT NULL AUTO_INCREMENT,
    firstName VARCHAR(24) NOT NULL,
    lastName VARCHAR(24) NOT NULL,
    username VARCHAR(24) NOT NULL,
    password VARCHAR(40) NOT NULL,
    PRIMARY KEY (userid)
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE products (
    productID INT UNSIGNED NOT NULL AUTO_INCREMENT,
    title VARCHAR(104) NOT NULL,
    picturePath VARCHAR(104) NULL,
    pictureThumb VARCHAR(104) NULL,
    creationDate DATE NOT NULL,
    closeDate DATE NULL,
    deleteDate DATE NULL,
    varPath VARCHAR(104) NULL,
    isPublic TINYINT(1) UNSIGNED NOT NULL DEFAULT '1',
    PRIMARY KEY (productID)
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

CREATE TABLE productUsers (
    productID INT UNSIGNED NOT NULL,
    userID INT UNSIGNED NOT NULL,
    permission VARCHAR(16) NOT NULL,
    PRIMARY KEY (productID,userID),
    FOREIGN KEY (productID) REFERENCES products (productID) ON DELETE RESTRICT ON UPDATE NO ACTION,
    FOREIGN KEY (userID) REFERENCES users (userID) ON DELETE RESTRICT ON UPDATE NO ACTION
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_unicode_ci;

O procedimento armazenado que estou usando é o seguinte:

CREATE PROCEDURE updateProductUsers (IN rUsername VARCHAR(24),IN rProductID INT UNSIGNED,IN rPerm VARCHAR(16))
BEGIN
    UPDATE productUsers
        INNER JOIN users
        ON productUsers.userID = users.userID
        SET productUsers.permission = rPerm
        WHERE users.username = rUsername
        AND productUsers.productID = rProductID;
END

Eu estava testando com php, mas o mesmo erro é dado com o SQLyog. Também testei recriar todo o banco de dados, mas não foi bom.

Qualquer ajuda será muito apreciada.

Manatax
fonte

Respostas:

220

O agrupamento padrão para os parâmetros do procedimento armazenado é utf8_general_cie você não pode misturar agrupamentos, portanto, você tem quatro opções:

Opção 1 : adicione COLLATEà sua variável de entrada:

SET @rUsername = aname COLLATE utf8_unicode_ci; -- COLLATE added
CALL updateProductUsers(@rUsername, @rProductID, @rPerm);

Opção 2 : adicione COLLATEà WHEREcláusula:

CREATE PROCEDURE updateProductUsers(
    IN rUsername VARCHAR(24),
    IN rProductID INT UNSIGNED,
    IN rPerm VARCHAR(16))
BEGIN
    UPDATE productUsers
        INNER JOIN users
        ON productUsers.userID = users.userID
        SET productUsers.permission = rPerm
        WHERE users.username = rUsername COLLATE utf8_unicode_ci -- COLLATE added
        AND productUsers.productID = rProductID;
END

Opção 3 : adicione-o à INdefinição de parâmetro:

CREATE PROCEDURE updateProductUsers(
    IN rUsername VARCHAR(24) COLLATE utf8_unicode_ci, -- COLLATE added
    IN rProductID INT UNSIGNED,
    IN rPerm VARCHAR(16))
BEGIN
    UPDATE productUsers
        INNER JOIN users
        ON productUsers.userID = users.userID
        SET productUsers.permission = rPerm
        WHERE users.username = rUsername
        AND productUsers.productID = rProductID;
END

Opção 4 : alterar o próprio campo:

ALTER TABLE users CHARACTER SET utf8 COLLATE utf8_general_ci;

A menos que você precise classificar os dados em ordem Unicode, sugiro alterar todas as suas tabelas para usar o utf8_general_ciagrupamento, pois não requer alterações de código e agilizará um pouco a classificação.

UPDATE : utf8mb4 / utf8mb4_unicode_ci agora é o método preferido de conjunto de caracteres / agrupamento. utf8_general_ci é desaconselhado, pois a melhoria de desempenho é insignificante. Consulte https://stackoverflow.com/a/766996/1432614

Ross Smith II
fonte
1
Também é possível adicionar COLLATE utf8_unicode_cia constantes string: SET @EMAIL = '[email protected]' COLLATE utf8_unicode_ci;. É especialmente útil se você estiver executando um script a partir de um console, onde a codificação padrão do console se aplica ao agrupamento das constantes de sequência.
gaborsch 5/05
Ou largue o banco de dados e crie um novo com utf8_general_ci; agrupamento.
Oleksii Kyslytsyn
2
Para referência futura, não altere todas as suas tabelas para utf8_general_ci, a menos que você entenda as diferenças entre os dois agrupamentos.
Manatax 31/07
1
@GaborSch Adicionando agrupar a variáveis ​​de string foi a solução para mim. Escrevi uma resposta detalhada antes de perceber seu comentário.
Nkatsar 10/10
estou recebendo o mesmo erro, exceto em (utf8mb4_unicode_ci, IMPLICIT)vez de (utf8_unicode_ci, IMPLICIT). estou raspando dados da web usando python e, em seguida, criando um arquivo CSV com os dados raspados, que depois processo com um arquivo PHP no meu servidor que carrega os dados no meu banco de dados. todas as minhas tabelas / colunas MySQL são agrupadas como utf8mb4_unicode_ci. o problema pode estar surgindo porque codifico os dados como utf8em python / csv?
Oldboy
27

Passei meio dia procurando respostas para um erro idêntico "Mistura ilegal de agrupamentos" com conflitos entre utf8_unicode_ci e utf8_general_ci.

Eu descobri que algumas colunas no meu banco de dados não foram especificamente agrupadas utf8_unicode_ci . Parece que o mysql agrupou implicitamente essas colunas utf8_general_ci .

Especificamente, a execução de uma consulta 'SHOW CREATE TABLE table1' gerou algo como o seguinte:

| table1 | CREATE TABLE `table1` (
`id` int(11) NOT NULL,
`col1` varchar(4) CHARACTER SET utf8 NOT NULL,
`col2` int(11) NOT NULL,
PRIMARY KEY (`col1`,`col2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

Observe que a linha 'col1' varchar (4) CHARACTER SET utf8 NOT NULL não possui um agrupamento especificado. Em seguida, executei a seguinte consulta:

ALTER TABLE table1 CHANGE col1 col1 VARCHAR(4) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL;

Isso resolveu o erro "Mistura ilegal de agrupamentos". Espero que isso ajude alguém por aí.

Nate Vaughan
fonte
7
obrigado. 'SHOW CREATE TABLE' é a maneira mais fácil de entender e corrigir a causa raiz do problema.
Joro
2
Observe também que especificar COLLATEa tabela inteira (ou seja ALTER TABLE table1 CHARSET utf8 COLLATE utf8_unicode_ci) não resolverá o problema ; isso deve ser feito para cada coluna (problemática).
Skippy le Grand Gourou
6

Eu tive um problema semelhante, mas ocorreu-me dentro do procedimento, quando meu parâmetro de consulta foi definido usando a variável eg SET @value='foo'.

O que estava causando isso foi incompatível collation_connectione agrupamento do banco de dados. Alterado collation_connectionpara combinar collation_databasee o problema desapareceu. Eu acho que essa é uma abordagem mais elegante do que adicionar COLLATE após param / value.

Resumindo: todos os agrupamentos devem corresponder. Use SHOW VARIABLESe certifique-se collation_connectione collation_databasejogo (também verificar agrupamento tabela usando SHOW TABLE STATUS [table_name]).

bpile
fonte
1
O mesmo problema aconteceu comigo, evitei alterar as variáveis ​​collation_YYY definindo o agrupamento diretamente na declaração da variável. SET @my_var = 'string1,string2' COLLATE utf8_unicode_ci;
Nkatsar 10/10
5

Um pouco semelhante à resposta @bpile, meu caso foi uma configuração de entrada my.cnf collation-server = utf8_general_ci. Depois que percebi isso (e depois de tentar tudo acima), mudei com força meu banco de dados para utf8_general_ci em vez de utf8_unicode_ci e foi isso:

ALTER DATABASE `db` CHARACTER SET utf8 COLLATE utf8_general_ci;
Sebas
fonte
1
É estranho que as configurações estejam espalhadas demais. Todos os padrões de agrupamento devem ser definidos no mesmo local.
Manatax 07/12/16
0

No meu próprio caso, tenho o seguinte erro

Combinação ilegal de agrupamentos (utf8_general_ci, IMPLICIT) e (utf8_unicode_ci, IMPLICIT) para a operação '='

$ this-> db-> select ("users.username como matric_no, CONCAT (nome de usuário, '', users.first_name, '', users.last_name) como nome completo") -> join ('users', 'users .username = classroom_students.matric_no ',' left ') -> where (' classroom_students.session_id ', $ session) -> where (' classroom_students.level_id ', $ level) -> where (' classroom_students.dept_id ', $ dept );

Após semanas de pesquisa no google, notei que os dois campos que estou comparando consistem em um nome de agrupamento diferente. O primeiro nome de usuário, ou seja, é utf8_general_ci enquanto o segundo é utf8_unicode_ci, então voltei à estrutura da segunda tabela e alterei o segundo campo (matric_no) para utf8_general_ci e funcionou como um encanto.

Teejaygenius
fonte
0

Apesar de encontrar um número enorme de perguntas sobre o mesmo problema ( 1 , 2 , 3 , 4 ), nunca encontrei uma resposta que levasse em consideração o desempenho, mesmo aqui.

Embora várias soluções de trabalho já tenham sido fornecidas, eu gostaria de fazer uma consideração de desempenho.

EDIT: Agradecemos à Manatax por apontar que a opção 1 não sofre problemas de desempenho.

O uso das opções 1 e 2 , também conhecida como abordagem de conversão COLLATE , pode levar a um gargalo em potencial, pois qualquer índice definido na coluna não será usado, causando uma verificação completa .

Embora eu não tenha experimentado a opção 3 , meu palpite é que sofrerá as mesmas consequências da opção 1 e 2.

Por fim, a opção 4 é a melhor opção para tabelas muito grandes quando viável. Quero dizer, não há outro uso que dependa do agrupamento original.

Considere esta consulta simplificada:

SELECT 
    *
FROM
    schema1.table1 AS T1
        LEFT JOIN
    schema2.table2 AS T2 ON T2.CUI = T1.CUI
WHERE
    T1.cui IN ('C0271662' , 'C2919021')
;

No meu exemplo original, eu tinha muito mais junções. Obviamente, tabela1 e tabela2 têm agrupamentos diferentes. Usando o operador agrupar para converter, levará a índices não sendo usados.

Veja a explicação sql na figura abaixo.

Explicação de consulta visual ao usar o elenco COLLATE

Por outro lado, a opção 4 pode tirar proveito de um possível índice e levar a consultas rápidas.

Na figura abaixo, você pode ver a mesma consulta sendo executada após a opção 4 aplicada , também conhecida como alteração do agrupamento de esquema / tabela / coluna.

Explicação de consulta visual após a intercalação ter sido alterada e, portanto, sem a conversão de intercalação

Em conclusão, se o desempenho é importante e você pode alterar o agrupamento da tabela, vá para a Opção 4 . Se você precisar atuar em uma única coluna, poderá usar algo como isto:

ALTER TABLE schema1.table1 MODIFY `field` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Raffaele
fonte
Obrigado por sua contribuição Raffaele, mas acredito que a opção 1 usaria o índice, porque você não está lançando a tabela, mas o valor de comparação antes mesmo de passá-la ao SP.
Manatax 23/10/18
Obrigado por apontar isso. Foi erro meu. Eu editei minha resposta de acordo.
Raffaele
0

Isso acontece quando uma coluna é definida explicitamente para um agrupamento diferente ou o agrupamento padrão é diferente na tabela consultada.

se você tiver muitas tabelas, você deseja alterar o agrupamento ao executar esta consulta:

select concat('ALTER TABLE ', t.table_name , ' CONVERT TO CHARACTER 
SET utf8 COLLATE utf8_unicode_ci;') from (SELECT table_name FROM 
information_schema.tables where table_schema='SCHRMA') t;

isso produzirá as consultas necessárias para converter todas as tabelas para usar o agrupamento correto por coluna

raam86
fonte
Também acontece quando (como no meu caso) seu agrupamento padrão para SP é diferente do agrupamento usado para a tabela consultada.
Manatax