Um nome pode aparecer como José em uma ferramenta e como José no sistema, uma busca por JOSE pode encontrar josé, e dois códigos aparentemente iguais podem não se encontrar em uma comparação. Esses comportamentos dependem de duas configurações dos textos no banco: o conjunto de caracteres, que define como cada letra é guardada, e a collation, que define como os textos são comparados e ordenados.
O conjunto de caracteres
O computador guarda cada caractere como um ou mais bytes. O conjunto de caracteres, chamado em inglês de character set, define essa correspondência. Os mais comuns no MySQL são:
- latin1: usa um byte por caractere e cobre as letras das línguas da Europa Ocidental, inclusive as acentuadas do português. Era o padrão até o MySQL 5.7.
- utf8mb4: usa de um a quatro bytes por caractere e cobre todos os caracteres, inclusive os emojis. É o padrão a partir do MySQL 8.
- utf8: nas versões antigas, é uma forma limitada do UTF-8, também chamada de utf8mb3, que usa no máximo três bytes por caractere e não guarda emojis.
Uma coluna em utf8mb3 não consegue guardar um emoji. No modo estrito, o MySQL recusa o valor com o erro 1366, de valor de texto incorreto, e, fora dele, troca o caractere por um ponto de interrogação. Esse caso pode aparecer quando um nome ou uma observação é copiado de um celular.
O utf8mb4 existe a partir do MySQL 5.5.3. Nas versões anteriores, como a 5.1, o maior conjunto disponível é o utf8 de três bytes, e nenhuma coluna consegue guardar um emoji.
A conexão também tem um conjunto de caracteres
Os dados guardados e a conexão que os lê têm configurações próprias. Quando a ferramenta que lê os dados usa um conjunto diferente do usado na gravação, os bytes são interpretados de outra forma. A letra e com acento agudo ocupa dois bytes em UTF-8. Quando esses bytes são lidos como latin1, cada byte vira um caractere, e a letra aparece como é. Por isso, o nome José aparece como José.
Dois comandos mostram as configurações em uso:
SHOW VARIABLES LIKE 'character_set%';: mostra o conjunto de caracteres do servidor, do banco e da conexão atual;SHOW CREATE TABLE clientes;: mostra o conjunto de caracteres e a collation da tabela e das colunas que tiverem configuração própria.
Erro de exibição ou dado gravado errado
Um texto estranho na tela pode ser apenas um erro de exibição, com o dado correto no banco, ou um dado já gravado com a codificação errada. A função HEX mostra os bytes realmente guardados e separa os dois casos:
SELECT nome, HEX(nome) FROM clientes WHERE id = 10;
Em uma coluna utf8mb4, a letra e com acento agudo, gravada corretamente, aparece nos bytes como C3A9. Se o HEX mostra C3A9 e a ferramenta exibe é, o problema está apenas na conexão da ferramenta. Se o HEX mostra C383C2A9, o próprio dado foi gravado com a codificação errada, e a correção exige um procedimento planejado sobre os dados.
As funções LENGTH e CHAR_LENGTH ajudam na mesma investigação. LENGTH conta os bytes, e CHAR_LENGTH conta os caracteres. Em utf8mb4, o nome José tem quatro caracteres e cinco bytes, porque a letra acentuada ocupa dois bytes.
A collation: as regras de comparação
A collation define se maiúsculas e minúsculas são iguais, se letras com e sem acento são iguais e em qual ordem os textos aparecem. O nome da collation indica essas regras:
- o final ci, de case insensitive, indica que maiúsculas e minúsculas são iguais na comparação;
- o final cs, de case sensitive, indica que maiúsculas e minúsculas são diferentes;
- a sigla ai, de accent insensitive, indica que letras com e sem acento são iguais;
- o final bin, de binário, indica a comparação byte a byte, em que qualquer diferença conta.
A collation padrão do MySQL 8, utf8mb4_0900_ai_ci, ignora acentos e maiúsculas. Por isso, a condição WHERE nome = 'jose' encontra José, JOSÉ e jose. Uma comparação exata, que considera cada diferença, pode ser feita na própria consulta:
SELECT * FROM clientes WHERE nome COLLATE utf8mb4_bin = 'José';
Essa forma impede o uso eficiente do índice da coluna e serve para conferências pontuais.
Efeitos da collation no suporte
- Valores repetidos: em uma coluna com collation ci, os valores ABC e abc são iguais. Uma regra de valor único na coluna recusa o segundo com o erro 1062, de entrada duplicada, mesmo com letras diferentes.
- Mistura de collations: uma comparação ou junção entre colunas com collations diferentes pode falhar com o erro 1267, de mistura ilegal de collations. A solução definitiva é alinhar a collation das colunas.
- Ordenação: a collation define a posição das letras acentuadas e das minúsculas em uma lista ordenada.
Espaços e caracteres invisíveis
Um espaço no fim de um código, ou um caractere invisível copiado de outro programa, faz dois textos aparentemente iguais serem diferentes. Nas collations do MySQL 8 com 0900 no nome, os espaços no fim do texto contam na comparação. Nas collations das versões anteriores, os espaços no fim são ignorados. Os espaços no início sempre contam.
A função TRIM remove os espaços do início e do fim, e a comparação entre LENGTH e CHAR_LENGTH, ou o HEX, revela caracteres que não aparecem na tela:
SELECT codigo, CHAR_LENGTH(codigo), HEX(codigo) FROM produtos WHERE codigo LIKE '%7891234%';
No suporte
Diante de um texto com caracteres estranhos, convém separar o erro de exibição do dado gravado errado antes de qualquer correção. A função HEX mostra os bytes guardados. Alterar os dados para corrigir um problema que está apenas na conexão da ferramenta estraga textos que estavam corretos.
Para guardar
- O conjunto de caracteres define como as letras viram bytes, e a collation define como os textos são comparados.
- O utf8mb4 cobre todos os caracteres, e o utf8 antigo não guarda emojis.
- Um conjunto de caracteres diferente na conexão faz um texto correto aparecer com caracteres estranhos.
- O HEX mostra os bytes e separa o erro de exibição do dado gravado errado.
- As collations ci tratam maiúsculas e minúsculas como iguais, e as ai também ignoram os acentos.
- Espaços e caracteres invisíveis podem impedir uma comparação.
Em uma ferramenta de consulta, o nome José aparece como José, mas o sistema mostra o nome corretamente. Qual é a explicação provável?
O dado está gravado corretamente em UTF-8, e a ferramenta interpreta os bytes com outro conjunto de caracteres, como o latin1. O problema está na conexão da ferramenta, e não nos dados. A função HEX confirma o diagnóstico quando a letra e com acento agudo aparece como C3A9.
Uma junção entre duas tabelas pela coluna codigo falha com o erro 1267, de mistura ilegal de collations. O que esse erro indica?
As duas colunas usam collations diferentes, e o MySQL não sabe qual regra aplicar à comparação. A solução definitiva é alinhar a collation das colunas. Em uma consulta pontual, a palavra COLLATE indica a regra que deve ser usada.