Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Mistura ilegal de agrupamentos do MySQL


É útil entender as seguintes definições:

  • Uma codificação de caracteres detalha como cada símbolo é representado em binário (e, portanto, armazenado no computador). Por exemplo, o símbolo é (U+00E9, letra minúscula E latina com agudo) é codificado como 0xc3a9 em UTF-8 (que o MySQL chama utf8 ) e 0xe9 em Windows-1252 (que o MySQL chama de latin1 ).

  • Um conjunto de caracteres é o alfabeto de símbolos que podem ser representados usando uma determinada codificação de caracteres. Confusamente, o termo também é usado para significar o mesmo que codificação de caracteres.

  • Uma coleção é uma ordenação em um conjunto de caracteres, para que as strings possam ser comparadas. Por exemplo:latin1_swedish_ci do MySQL collation trata as variações mais acentuadas de um caractere como equivalente ao caractere base, enquanto seu latin1_general_ci collation irá ordená-los antes do próximo caractere base, mas não equivalente (há outras diferenças mais significativas também:como a ordem dos caracteres como å , ä , ö e ß ).

O MySQL decidirá qual agrupamento deve ser aplicado a uma determinada expressão conforme documentado em Agrupamento de expressões :em particular, o agrupamento de uma coluna tem precedência sobre o de um literal de string.

O WHERE cláusula da sua consulta compara as seguintes strings:

  1. um valor em fos_user.username , codificado no conjunto de caracteres da coluna (Windows-1252) e expressando uma preferência por seu agrupamento latin1_swedish_ci (com um valor de coercibilidade de 2); com

  2. a string literal 'Nrv⧧Kasi' , codificado no conjunto de caracteres da conexão (UTF-8, conforme configurado pelo Doctrine) e expressando preferência pelo agrupamento da conexão utf8_general_ci (com um valor de coercibilidade de 4).

Como a primeira dessas strings tem um valor de coercibilidade menor que a segunda, o MySQL tenta realizar a comparação usando o agrupamento dessa string:latin1_swedish_ci . Para fazer isso, o MySQL tenta converter a segunda string para latin1 —mas desde o caractere não existir nesse conjunto de caracteres, a comparação falhará.

Aviso


Deve-se fazer uma pausa por um momento para considerar como a coluna está atualmente codificada:você está tentando filtrar registros onde fos_user.username é igual a uma string que contém um caractere que não pode existe nessa coluna !

Se você acredita que a coluna sim contém esses caracteres, então você provavelmente escreveu na coluna enquanto a codificação do caractere de conexão estava definida para algo (por exemplo, latin1 ) que fez com que o MySQL interpretasse a sequência de bytes recebida como caracteres que estão todos no conjunto de caracteres Windows-1252.

Se este for o caso, antes de continuar, você deve corrigir seus dados!

  1. converter essas colunas para a codificação de caracteres que foi usada na inserção de dados, se diferente da codificação incumbente:
    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET foo;
    

  2. elimine as informações de codificação associadas a essas colunas convertendo-as para o binary conjunto de caracteres:
    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET binary;
    

  3. associe a essas colunas a codificação na qual os dados foram realmente transmitidos, convertendo-os no conjunto de caracteres relevante.
    ALTER TABLE fos_users MODIFY username VARCHAR(123) CHARACTER SET bar;
    

Observe que, se estiver convertendo de uma codificação multibyte, pode ser necessário aumentar o tamanho da coluna (ou até mesmo alterar seu tipo) para acomodar o comprimento máximo possível da string convertida.

Uma vez que se tenha certeza de que as colunas estão codificadas corretamente, pode-se forçar a comparação a ser realizada usando um agrupamento Unicode por:

  • convertendo explicitamente o valor fos_user.username para um conjunto de caracteres Unicode:
    WHERE CONVERT(fos_user.username USING utf8) = ?
    

  • forçando a string literal a ter um valor de coercibilidade menor que a coluna (causará uma conversão implícita do valor da coluna para UTF-8):
    WHERE fos_user.username = ? COLLATE utf8_general_ci
    

Ou pode-se, como você diz, converter permanentemente as colunas em uma codificação Unicode e definir seu agrupamento adequadamente.

A principal consideração é que as codificações Unicode ocupam mais espaço do que conjuntos de caracteres de byte único, portanto:

  • mais armazenamento pode ser necessário;

  • as comparações podem ser mais lentas; e

  • os comprimentos do prefixo do índice podem precisar ser ajustados (observe que o máximo está em bytes, portanto, pode representar menos caracteres do que anteriormente).

Além disso, esteja ciente de que, conforme documentado em ALTER TABLE Sintaxe :