Como alterar o CHARACTER SET (e COLLATION) em um banco de dados?

172

Nosso programador anterior definiu o agrupamento errado em uma tabela (Mysql). Ele o configurou com agrupamento em latim, quando deveria ser UTF8, e agora tenho problemas. Todos os discos com caracteres chineses e japoneses passam para ??? personagem.

É possível alterar o agrupamento e recuperar os detalhes do personagem?

Jeg Bagus
fonte
possível duplicação do MySQL alterar tabela Collation
kenorb 03/03
O que o agrupamento tem a ver com '???' conjunto de caracteres? Eu pensei que isso tinha a ver com o conjunto de caracteres?
peterchaula
Estou mudando o título para refletir a intenção. Alterar o agrupamento padrão para um banco de dados é muito menor do que o desejado.
Rick James

Respostas:

365

alterar agrupamento do banco de dados:

ALTER DATABASE <database_name> CHARACTER SET utf8 COLLATE utf8mb4_0900_ai_ci;

alterar agrupamento da tabela:

ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8 COLLATE utf8mb4_0900_ai_ci;

alterar o agrupamento da coluna:

ALTER TABLE <table_name> MODIFY <column_name> VARCHAR(255) CHARACTER SET utf8 COLLATE utf8mb4_0900_ai_ci;

O que utf8mb4_0900_ai_cisignificam as partes ?

3 bytes -- utf8
4 bytes -- utf8mb4 (new)
v4.0 --   _unicode_
v5.20 --  _unicode_520_
v9.0 --   _0900_ (new)
_bin      -- just compare the bits; don't consider case folding, accents, etc
_ci       -- explicitly case insensitive (A=a) and implicitly accent insensitive (a=á)
_ai_ci    -- explicitly case insensitive and accent insensitive
_as (etc) -- accent-sensitive (etc)
_bin         -- simple, fast
_general_ci  -- fails to compare multiple letters; eg ss=ß, somewhat fast
...          -- slower
_0900_       -- (8.0) much faster because of a rewrite

Mais informações:

Timo Huovinen
fonte
4
Cuidado CHARACTER SET utf8será o padrão para utf8_general_ci, mas você também pode definir o agrupamento como esta ALTER DATABASE <database_name> CHARACTER SET utf8 COLLATE utf8_unicode_ci;se necessário
KCD
1
... e eu recomendo que você testecreate table testit(a varchar(1)); show create table testit \G drop table testit;
KCD
2
Só quero mencionar que o segundo mudará o agrupamento para utf8_general_ci; se você quiser mudá-lo para utf8_unicode_ci, você pode definir agrupamento: ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;. Isso funciona em tabelas exatamente da mesma forma que em bancos de dados, como o @KCD apontou.
sábio
9
É melhor fazer o seguinte para obter suporte total ao utf8 ALTER DATABASE <database_name> CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci. Você deve fazer o mesmo nas outras duas instruções.
Greeso
Você realmente precisa usar "ALTER TABLE <table_name> MODIFY <column_name> ...". De acordo com dev.mysql.com/doc/refman/5.5/en/alter-table.html , parece que "ALTER TABLE <nome_da_tabela> CONVERTIR NO CONJUNTO DE PERSONAGENS ..." também altera as colunas? Ou talvez eu não esteja lendo / entendendo o manual corretamente.
hansfn
49

Aqui está como alterar todos os bancos de dados / tabelas / colunas. Execute essas consultas e elas produzirão todas as consultas subsequentes necessárias para converter todo o esquema em utf8. Espero que isto ajude!

- Alterar agrupamento padrão do DATABASE

SELECT DISTINCT concat('ALTER DATABASE `', TABLE_SCHEMA, '` CHARACTER SET utf8 COLLATE utf8_unicode_ci;')
from information_schema.tables
where TABLE_SCHEMA like  'database_name';

- Alterar TABELA agrupamento / conjunto de caracteres

SELECT concat('ALTER TABLE `', TABLE_SCHEMA, '`.`', table_name, '` CHARACTER SET utf8 COLLATE utf8_unicode_ci;')
from information_schema.tables
where TABLE_SCHEMA like 'database_name';

- Alterar conjunto de agrupamento / conjunto de caracteres COLUMN

SELECT concat('ALTER TABLE `', t1.TABLE_SCHEMA, '`.`', t1.table_name, '` MODIFY `', t1.column_name, '` ', t1.data_type , '(' , t1.CHARACTER_MAXIMUM_LENGTH , ')' , ' CHARACTER SET utf8 COLLATE utf8_unicode_ci;')
from information_schema.columns t1
where t1.TABLE_SCHEMA like 'database_name' and t1.COLLATION_NAME = 'old_charset_name';
David Whittaker
fonte
Boa. ! Há cerca de uma hora que estou tentando resolver o mesmo problema. Eu uso esses 3 comandos e vi que o charset havia mudado. Mas o principal problema permanece para mim. Se eu escrevi diretamente no banco de dados, tudo mostra bem no meu navegador. Mas se eu adicionasse algum conteúdo do formulário do site, o resultado no banco de dados seria apenas ??????. Há algo que eu deva considerar? Meu aplicativo da web é um aplicativo .NET MVC.
Tchaps
Salvando em consultas úteis para projetos futuros.
Manatax 01/03
Sugeri algumas edições porque essas consultas automatizadas ainda não eram seguras. Ainda existe um problema com o CHARACTER_MAXIMUM_LENGTH: O original pode estar muito alto quando você muda de, por exemplo, latin1_swedish_ci para utf8_unicode_ci.
Ruben
1
Esta é uma excelente resposta. Eu tenho três comentários / perguntas: 1) Por que o uso de "t1" no código da COLUNA? Não vejo necessidade disso. 2) Por que "t1.data_type, '(', t1.CHARACTER_MAXIMUM_LENGTH, ')'" "e não apenas" t1.column_type "? 3) Por que a mistura de maiúsculas e minúsculas - TABLE_SCHEMA vs table_name e assim por diante?
hansfn
25

Cuidado que no Mysql, o utf8conjunto de caracteres é apenas um subconjunto do conjunto de caracteres UTF8 real. Para salvar um byte de armazenamento, a equipe do Mysql decidiu armazenar apenas três bytes de caracteres UTF8 em vez dos quatro bytes completos. Isso significa que alguns idiomas e emoji do leste asiático não são totalmente suportados. Para garantir que você possa armazenar todos os caracteres UTF8, use o utf8mb4tipo de dados e / utf8mb4_binou utf8mb4_general_cino Mysql.

bluecollarcoder
fonte
1
Até agora, é recomendável usar em utf8mb4_unicode_civez de utf8mb4_general_ci. Veja stackoverflow.com/questions/766809/… e drupal.stackexchange.com/questions/166405/…
Robin van Baalen
6

Adicionando ao que David Whittaker postou, criei uma consulta que gera a declaração completa de alteração de tabela e colunas que converterá cada tabela. Pode ser uma boa ideia executar

SET SESSION group_concat_max_len = 100000;

primeiro para garantir que o concat do seu grupo não ultrapasse o limite muito pequeno, como visto aqui .

     SELECT a.table_name, concat('ALTER TABLE ', a.table_schema, '.', a.table_name, ' DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci, ',
        group_concat(distinct(concat(' MODIFY ',  column_name, ' ', column_type, ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ', if (is_nullable = 'NO', ' NOT', ''), ' NULL ',
        if (COLUMN_DEFAULT is not null, CONCAT(' DEFAULT \'', COLUMN_DEFAULT, '\''), ''), if (EXTRA != '', CONCAT(' ', EXTRA), '')))), ';') as alter_statement
    FROM information_schema.columns a
    INNER JOIN INFORMATION_SCHEMA.TABLES b ON a.TABLE_CATALOG = b.TABLE_CATALOG
        AND a.TABLE_SCHEMA = b.TABLE_SCHEMA
        AND a.TABLE_NAME = b.TABLE_NAME
        AND b.table_type != 'view'
    WHERE a.table_schema = ? and (collation_name = 'latin1_swedish_ci' or collation_name = 'utf8mb4_general_ci')
    GROUP BY table_name;

Uma diferença aqui entre a resposta anterior é que o uso de utf8 em vez de ut8mb4 e o uso de t1.data_type com t1.CHARACTER_MAXIMUM_LENGTH não funcionaram para enumerações. Além disso, minha consulta exclui visualizações, pois elas terão que ser alteradas separadamente.

Eu simplesmente usei um script Perl para retornar todos esses alteradores como uma matriz e iterou sobre eles, corrigi as colunas que eram muito longas (geralmente elas eram varchar (256) quando os dados geralmente tinham apenas 20 caracteres nelas, o que era uma solução fácil )

Encontrei alguns dados corrompidos ao alterar de latin1 -> utf8mb4. Parecia que os caracteres latin1 codificados em utf8 nas colunas seriam enganados na conversão. Simplesmente retive os dados das colunas que sabia que seriam um problema na memória antes e depois da alteração e os comparei e gerei instruções de atualização para corrigir os dados.

Jacob Hundley
fonte
4

aqui descreve bem o processo. No entanto, alguns dos personagens que não se encaixavam no espaço latino se foram para sempre. UTF-8 é um SUPERSET de latin1. Não é o contrário. A maioria caberá no espaço de byte único, mas nenhum indefinido não (verifique uma lista de latin1 - nem todos os 256 caracteres são definidos, dependendo da definição latin1 do mysql)

MJB
fonte