Detalhes de charset e collation no MySQL

02/03/2012 por

Trabalhar com definições de charset na web é um problema para quem vive entre projetos com codificação ISO-8859-1 (latin) e UTF-8. Quem não tem muito conhecimento sobre isso começa trabalhando apenas com ISO-8859-1 mas na primeira interação com webservices ou primeira conexão com banco de dados de um sistema um pouco mais elaborado (preocupado com questões de internacionalização) a dor de cabeça pode aparecer, parecendo com um cóo.

Note que um problema com a exibição de caracteres em uma página pode ocorrer não só pela configuração do charset do banco de dados, mas também por arquivos de aplicação com o charset errado, uma configuração de conexão com banco de dados incoerente com o resto do projeto e até uma meta tag HTML ou resposta com um cabeçalho HTTP incorreto.

Charset e Collation

Charset e collation não são palavras diferentes que significam a mesma coisa, um charset é um conjunto de caracteres e suas respectivas representações binárias, enquanto um collation é um conjunto de regras para comparação dos caracteres representados, para exemplificar: é o collation que determina que o "A" é igual ao "a" em um collation case insensitive.

Exemplos de charset: latin1, utf8, ascii, cp1250, koi8r (descubra outros com SHOW CHARACTER SET)

Exemplos de collation: latin1_general_ci, latin1_spanish_ci, utf8_unicode_ci, utf8_general_ci (descubra outros com SHOW COLLATION)

Se você observar problemas com caracteres aparecendo incorretamente no meio de sua página, o problema é o charset. Se suas comparações em suas queryes estiverem se comportando "estranhamente" verifique se o collation da sua tabela (e campos, sim, no MySQL é possível definir um collation apenas para um campo da sua tabela).

Collations normalmente finalizam com _ci quando case insensitive, _cs quando case sensitive e _bin quando binário.

Definindo charset e collation no MySQL

Para definir o charset após a conexão use SET NAMES 'utf8';, neste exemplo utilizado para definir UTF-8.

Para definir o charset e o collation no momento da criação da base de dados utilize CREATE DATABASE minhabase DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;. Note que qualquer tabela criada dentro dessa base de dados que não possuir definição do charset e/ou I{collation}i irá assumir o padrão da base de dados.

Se você criar uma base de dados com as definições incorretas a conversão é bem simples: ALTER TABLE tabela CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;. Todo conteúdo dessa tabela ficará com o charset escolhido. Outros comandos para alterar estas definições você pode encontrar no site do MySQL

Como resolver o problema do "Illegal mix of collations" em uma query

Se durante uma query você se deparar com algo como Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8_general_ci,COERCIBLE) você com certeza estará tentando comparar duas colunas com collations diferentes, que o MySQL não consegue comparar. Isso é muito comum acontecer em views que utilizam tabelas em bases de dados diferentes.

Para resolver isso você pode tentar converter o campo em sua query adicionando o COLLATE desejado, por exemplo: SELECT * FROM campo1 COLLATE latin1_swedish_ci = campo2. É importante lembrar que isso só ajusta o collation se o charset for compatível, você não pode converter para latin1_swedish_ci se o i{charset}i do campo1 for UTF-8.

Para ajustar o charset de um campo podemos utilizar a função CONVERT no com a codificação desejada: SELECT * FROM CONVERT(campo1 USING utf8) COLLATE utf8_bin = campo2.

Dicas para evitar problemas

As soluções acima são válidas apenas para ajustes pequenos e emergenciais pois podem afetar a velocidade de resposta de sua i{query}i e a "bagunça" continuará lá, escondida.

A melhor coisa que você pode fazer é uniformizar sua base de dados e evitar que qualquer tabela/campo fique com o i{charset}i ou i{collation}i diferente do restante do sistema. Note que isso é uma boa prática, não uma regra.

Referências