Comment changer le JEU DE CARACTÈRES (et la COLLATION) dans une base de données?

172

Notre programmeur précédent a défini le mauvais classement dans une table (Mysql). Il l'a mis en place avec le classement latin, alors qu'il devrait être UTF8, et maintenant j'ai des problèmes. Chaque disque avec des caractères chinois et japonais se transforme en ??? personnage.

Est-il possible de changer le classement et de récupérer le détail du personnage?

Jeg Bagus
la source
duplicata possible de MySql alter table Collation
kenorb
Qu'est-ce que le classement a à voir avec «???» jeu de caractères? Je pensais que c'était à voir avec le jeu de caractères?
peterchaula
Je change le titre pour refléter l'intention. La modification du classement par défaut d'une base de données est bien moindre que ce qui était souhaité.
Rick James

Réponses:

366

modifier le classement de la base de données:

ALTER DATABASE <database_name> CHARACTER SET utf8 COLLATE utf8mb4_0900_ai_ci;

modifier le classement de la table:

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

modifier le classement des colonnes:

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

Que utf8mb4_0900_ai_cisignifient les parties de ?

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

Plus d'informations:

Timo Huovinen
la source
5
Méfiez CHARACTER SET utf8- vous sera par défaut, utf8_general_cimais vous pouvez également définir le classement comme celui-ci ALTER DATABASE <database_name> CHARACTER SET utf8 COLLATE utf8_unicode_ci;si nécessaire
KCD
1
... et je vous recommande de le testercreate table testit(a varchar(1)); show create table testit \G drop table testit;
KCD
2
Je veux juste mentionner que le second changera la collation en utf8_general_ci; si vous voulez changer pour utf8_unicode_ci, vous pouvez définir la collation: ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;. Cela fonctionne sur les tables exactement de la même manière que sur les bases de données, comme @KCD l'a souligné.
plus sage
9
Il est préférable de faire ce qui suit pour une prise en charge complète de utf8 ALTER DATABASE <database_name> CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci. Vous devez faire de même pour les deux autres instructions.
Greeso
Avez-vous vraiment besoin d'utiliser "ALTER TABLE <nom_table> MODIFY <nom_colonne> ...". Selon dev.mysql.com/doc/refman/5.5/en/alter-table.html, il semble que "ALTER TABLE <table_name> CONVERT TO CHARACTER SET ..." change également les colonnes? Ou peut-être que je ne lis / ne comprends pas correctement le manuel.
hansfn
49

Voici comment changer toutes les bases de données / tables / colonnes. Exécutez ces requêtes et elles afficheront toutes les requêtes suivantes nécessaires pour convertir l'ensemble de votre schéma en utf8. J'espère que cela t'aides!

- Modifier le classement par défaut de la base de données

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

- Modifier le classement / le jeu de caractères de TABLE

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';

- Modifier le classement / le jeu de caractères 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
la source
Bien. ! Cela fait environ une heure que j'essaye de résoudre le même problème. J'utilise ces 3 commandes et j'ai vu que le jeu de caractères avait changé. Mais le principal problème reste pour moi. Si j'écris directement dans la base de données, tout s'affiche bien dans mon navigateur. Mais si j'ai ajouté du contenu à partir du formulaire du site Web, le résultat dans la base de données est juste ??????. Y a-t-il quelque chose que je devrais considérer? Mon application Web est une application .NET MVC.
Tchaps du
Enregistrement dans des requêtes utiles pour de futurs projets.
Manatax
J'ai suggéré quelques modifications car ces requêtes automatisées n'étaient pas encore tout à fait sûres à utiliser. Il y a toujours un problème avec le CHARACTER_MAXIMUM_LENGTH: celui d'origine peut être trop élevé lorsque vous passez par exemple de latin1_swedish_ci à utf8_unicode_ci.
Ruben
1
C'est une excellente réponse. J'ai trois commentaires / questions: 1) Pourquoi utiliser "t1" dans le code COLUMN? Je n'en vois aucun besoin. 2) Pourquoi "t1.data_type, '(', t1.CHARACTER_MAXIMUM_LENGTH, ')'" et pas seulement "t1.column_type"? 3) Pourquoi le mélange de majuscules et de minuscules - TABLE_SCHEMA vs table_name et ainsi de suite?
hansfn
25

Attention, dans Mysql, le utf8jeu de caractères n'est qu'un sous-ensemble du jeu de caractères UTF8 réel. Afin d'économiser un octet de stockage, l'équipe Mysql a décidé de ne stocker que trois octets de caractères UTF8 au lieu des quatre octets complets. Cela signifie que certaines langues et emoji d'Asie de l'Est ne sont pas entièrement pris en charge. Pour vous assurer que vous pouvez stocker tous les caractères UTF8, utilisez le utf8mb4type de données et / utf8mb4_binou utf8mb4_general_cidans Mysql.

bluecollarcoder
la source
1
À présent, il est recommandé d'utiliser à la utf8mb4_unicode_ciplace de utf8mb4_general_ci. Voir stackoverflow.com/questions/766809/… et drupal.stackexchange.com/questions/166405/…
Robin van Baalen
6

En plus de ce que David Whittaker a publié, j'ai créé une requête qui génère la table complète et l'instruction alter des colonnes qui convertira chaque table. Ce peut être une bonne idée de courir

SET SESSION group_concat_max_len = 100000;

d'abord pour vous assurer que votre groupe concat ne dépasse pas la très petite limite vue ici .

     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;

Une différence ici entre la réponse précédente est qu'elle utilisait utf8 au lieu de ut8mb4 et l'utilisation de t1.data_type avec t1.CHARACTER_MAXIMUM_LENGTH ne fonctionnait pas pour les énumérations. De plus, ma requête exclut les vues car celles-ci devront être modifiées séparément.

J'ai simplement utilisé un script Perl pour renvoyer toutes ces modifications sous forme de tableau et les ai itérées, j'ai corrigé les colonnes trop longues (généralement elles étaient varchar (256) lorsque les données ne contenaient généralement que 20 caractères, donc c'était une solution facile. ).

J'ai trouvé que certaines données étaient corrompues lors de la modification de latin1 -> utf8mb4. Il semble que les caractères latin1 encodés en utf8 dans les colonnes soient gaffés lors de la conversion. J'ai simplement conservé les données des colonnes dont je savais qu'elles allaient être un problème en mémoire avant et après le changement et les ai comparées et j'ai généré des instructions de mise à jour pour corriger les données.

Jacob Hundley
la source
4

décrit ici bien le processus. Cependant, certains des personnages qui ne correspondaient pas à l'espace latin ont disparu pour toujours. UTF-8 est un SUPERSET de latin1. Pas l'inverse. La plupart tiendront dans un espace d'un octet, mais tous ceux non définis ne le seront pas (vérifiez une liste de latin1 - tous les 256 caractères ne sont pas définis, selon la définition latin1 de mysql)

MJB
la source