Come modificare il SET DI CARATTERI (e COLLATION) in un database?


172

Il nostro programmatore precedente ha impostato le regole di confronto errate in una tabella (Mysql). Lo ha impostato con regole di confronto latine, quando dovrebbe essere UTF8, e ora ho problemi. Ogni disco con carattere cinese e giapponese si trasforma in ??? carattere.

È possibile cambiare le regole di confronto e recuperare i dettagli del personaggio?


possibile duplicato di MySql alter table Collation
kenorb

Cosa c'entra la collazione con '???' set di caratteri? Pensavo avesse a che fare con il set di caratteri?
peterchaula,

Sto cambiando il titolo per riflettere l'intento. La modifica delle regole di confronto predefinite per un database è molto inferiore a quanto desiderato.
Rick James,

Risposte:


365

modifica regole di confronto database:

ALTER DATABASE <database_name> CHARACTER SET utf8 COLLATE utf8mb4_0900_ai_ci;

cambia la tabella di confronto:

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

cambia regole di confronto colonne:

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

Cosa significano le parti di utf8mb4_0900_ai_ci?

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

Ulteriori informazioni:


4
Attenzione CHARACTER SET utf8, utf8_general_cima puoi anche definire le regole di confronto in questo modo, ALTER DATABASE <database_name> CHARACTER SET utf8 COLLATE utf8_unicode_ci;se necessario
KCD,

1
... e ti consiglio di provarlocreate table testit(a varchar(1)); show create table testit \G drop table testit;
KCD,

2
Voglio solo ricordare che il secondo cambierà il confronto in utf8_general_ci; se si desidera cambiarlo utf8_unicode_ci, è possibile definire le regole di confronto: ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;. Funziona su tabelle esattamente come su database, come ha sottolineato @KCD.
più saggio il

9
È meglio fare quanto segue per il pieno supporto di utf8 ALTER DATABASE <database_name> CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci. Dovresti fare lo stesso per le altre due affermazioni.
Greeso,

Hai davvero bisogno di usare "ALTER TABLE <table_name> MODIFY <column_name> ...". Secondo dev.mysql.com/doc/refman/5.5/en/alter-table.html sembra che "ALTER TABLE <table_name> CONVERT TO CHARACTER SET ..." cambi anche le colonne? O forse non sto leggendo / capendo il manuale correttamente.
hansfn,

49

Ecco come modificare tutti i database / tabelle / colonne. Esegui queste query e verranno visualizzate tutte le query successive necessarie per convertire l'intero schema in utf8. Spero che questo ti aiuti!

- Modifica regole di confronto predefinite 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';

- Cambia TABLE Fascicolazione / Set caratteri

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

- Cambia COLUMN Collation / Char Set

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

Buona. ! Sono circa un'ora che sto cercando di risolvere lo stesso problema. Uso questi 3 comandi e ho visto che il set di caratteri è cambiato. Ma il problema principale rimane per me. Se ho scritto direttamente nel database, allora tutto appare bene nel mio browser. Ma se ho aggiunto alcuni contenuti dal modulo del sito Web, il risultato nel database è solo ??????. C'è qualcosa che dovrei considerare? La mia app Web è un'app .NET MVC.
Arriva l'

Salvataggio in query utili per progetti futuri.
Manatax

Ho suggerito alcune modifiche perché queste query automatizzate non erano ancora abbastanza sicure da usare. C'è ancora un problema con CHARACTER_MAXIMUM_LENGTH: quello originale può essere troppo alto quando passi da ad esempio latin1_swedish_ci a utf8_unicode_ci.
Ruben,

1
Questa è una risposta eccellente Ho tre commenti / domande: 1) Perché l'uso di "t1" nel codice COLUMN? Non ne vedo alcun bisogno. 2) Perché "t1.data_type, '(', t1.CHARACTER_MAXIMUM_LENGTH, ')'" e non solo "t1.column_type"? 3) Perché la combinazione di maiuscole e minuscole - TABLE_SCHEMA vs table_name e così via?
hansfn,

25

Attenzione che in Mysql, il utf8set di caratteri è solo un sottoinsieme del set di caratteri UTF8 reale. Per salvare un byte di memoria, il team di Mysql ha deciso di memorizzare solo tre byte di un carattere UTF8 anziché i quattro byte completi. Ciò significa che alcune lingue ed emoji dell'Asia orientale non sono completamente supportate. Per assicurarti di poter memorizzare tutti i caratteri UTF8, usa il utf8mb4tipo di dati e utf8mb4_bino utf8mb4_general_ciin Mysql.


1
Ormai, si consiglia di utilizzare utf8mb4_unicode_ciinvece di utf8mb4_general_ci. Vedere stackoverflow.com/questions/766809/... e drupal.stackexchange.com/questions/166405/...
Robin van Baalen

6

Aggiungendo ciò che ha pubblicato David Whittaker, ho creato una query che genera la dichiarazione completa di alterazione di tabelle e colonne che convertirà ogni tabella. Potrebbe essere una buona idea correre

SET SESSION group_concat_max_len = 100000;

per prima cosa assicurati che il tuo gruppo non superi il limite molto piccolo visto qui .

     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;

Una differenza qui tra la risposta precedente è che stava usando utf8 invece di ut8mb4 e usando t1.data_type con t1.CHARACTER_MAXIMUM_LENGTH non ha funzionato per gli enum. Inoltre, la mia query esclude le viste poiché dovranno essere modificate separatamente.

Ho semplicemente usato uno script Perl per restituire tutti questi alter come array e ho iterato su di essi, riparando le colonne che erano troppo lunghe (in genere erano varchar (256) quando i dati generalmente contenevano solo 20 caratteri, quindi era una soluzione semplice ).

Ho trovato alcuni dati danneggiati durante l'alterazione da latin1 -> utf8mb4. Sembrava che i caratteri latin1 codificati in utf8 in colonne si sarebbero fatti ingannare nella conversione. Ho semplicemente tenuto i dati dalle colonne che sapevo sarebbero stati un problema in memoria da prima e dopo l'alter e li ho confrontati e generato istruzioni di aggiornamento per correggere i dati.


4

qui descrive bene il processo. Tuttavia, alcuni dei personaggi che non si adattavano allo spazio latino sono andati per sempre. UTF-8 è un SUPERSET di latino1. Non il contrario. La maggior parte si adatterà nello spazio a byte singolo, ma quelli non definiti non lo faranno (controlla un elenco di latin1 - non tutti i 256 caratteri sono definiti, a seconda della definizione latin1 di mysql)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.