¿Cómo cambiar el CONJUNTO DE CARACTERES (y COLLACIÓN) en una base de datos?

172

Nuestro programador anterior estableció la clasificación incorrecta en una tabla (Mysql). Lo configuró con intercalación latina, cuando debería ser UTF8, y ahora tengo problemas. Todos los discos con caracteres chinos y japoneses recurren a ??? personaje.

¿Es posible cambiar la colación y recuperar los detalles del personaje?

Jeg Bagus
fuente
posible duplicado de colación de tabla de alteración de
MySql
¿Qué tiene que ver la colación con '???' ¿conjunto de caracteres? Pensé que tenía que ver con el conjunto de caracteres?
Peterchaula
Estoy cambiando el título para reflejar la intención. Cambiar la clasificación predeterminada para una base de datos es mucho menor de lo deseado.
Rick James

Respuestas:

366

cambiar la clasificación de la base de datos:

ALTER DATABASE <database_name> CHARACTER SET utf8 COLLATE utf8mb4_0900_ai_ci;

cambiar la clasificación de la tabla:

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

cambiar colación de columna:

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

¿Qué significan las partes de 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

Más información:

Timo Huovinen
fuente
55
Tenga cuidado CHARACTER SET utf8con el valor predeterminado, utf8_general_cipero también puede definir la intercalación de esta manera ALTER DATABASE <database_name> CHARACTER SET utf8 COLLATE utf8_unicode_ci;si es necesario
KCD
1
... y te recomiendo que lo create table testit(a varchar(1)); show create table testit \G drop table testit;
pruebes
2
Solo quiero mencionar que el segundo cambiará la colación a utf8_general_ci; si desea cambiarlo a utf8_unicode_ci, se puede definir el cotejo: ALTER TABLE <table_name> CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;. Esto funciona en tablas exactamente igual que en bases de datos, como ha señalado @KCD.
más sabio
9
Es mejor hacer lo siguiente para obtener soporte completo de utf8 ALTER DATABASE <database_name> CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci. Debe hacer lo mismo para las otras dos declaraciones.
Greeso
¿Realmente necesita usar "ALTER TABLE <table_name> MODIFY <column_name> ...". De acuerdo con dev.mysql.com/doc/refman/5.5/en/alter-table.html parece "ALTER TABLE <table_name> CONVERT TO CHARACTER SET ..." también cambia las columnas? O tal vez no estoy leyendo / entendiendo el manual correctamente.
hansfn
49

Aquí está cómo cambiar todas las bases de datos / tablas / columnas. Ejecute estas consultas y generarán todas las consultas posteriores necesarias para convertir todo su esquema a utf8. ¡Espero que esto ayude!

- Cambiar la clasificación predeterminada de la BASE DE DATOS

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

- Cambiar TABLE Collation / Char Set

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

- Cambiar COLUMNA Intercalación / Conjunto de caracteres

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
fuente
Bueno. ! Hace aproximadamente una hora que intento resolver el mismo problema. Utilizo estos 3 comandos, y vi que el juego de caracteres había cambiado. Pero el principal problema me queda. Si escribí directamente en la base de datos, todo se muestra bien en mi navegador. Pero si agregué algún contenido del formulario del sitio web, el resultado en la base de datos es solo ??????. ¿Hay algo que deba considerar? Mi aplicación web es una aplicación .NET MVC.
Tchaps
Guardar en consultas útiles para futuros proyectos.
Manatax
Sugerí algunas ediciones porque estas consultas automáticas aún no eran del todo seguras de usar. Todavía hay un problema con CHARACTER_MAXIMUM_LENGTH: el original puede ser demasiado alto cuando cambias, por ejemplo, de latin1_swedish_ci a utf8_unicode_ci.
Ruben
1
Esta es una excelente respuesta. Tengo tres comentarios / preguntas: 1) ¿Por qué el uso de "t1" en el código COLUMNA? No veo ninguna necesidad de ello. 2) ¿Por qué "t1.data_type, '(', t1.CHARACTER_MAXIMUM_LENGTH, ')'" y no solo "t1.column_type"? 3) ¿Por qué la combinación de mayúsculas y minúsculas - TABLE_SCHEMA vs table_name y así sucesivamente?
hansfn
25

Tenga en cuenta que en Mysql, el utf8juego de caracteres es solo un subconjunto del juego de caracteres UTF8 real. Para guardar un byte de almacenamiento, el equipo de Mysql decidió almacenar solo tres bytes de caracteres UTF8 en lugar de los cuatro bytes completos. Eso significa que algunos idiomas y emoji de Asia oriental no son totalmente compatibles. Para asegurarse de que puede almacenar todos los caracteres UTF8, use el utf8mb4tipo de datos utf8mb4_bino utf8mb4_general_ciMysql.

codificador azul
fuente
1
Por ahora, se recomienda usar en utf8mb4_unicode_cilugar de utf8mb4_general_ci. Ver stackoverflow.com/questions/766809/… y drupal.stackexchange.com/questions/166405/…
Robin van Baalen
6

Además de lo que David Whittaker publicó, he creado una consulta que genera la tabla completa y la declaración de modificación de columnas que convertirá cada tabla. Puede ser una buena idea correr

SET SESSION group_concat_max_len = 100000;

primero para asegurarse de que su grupo concat no exceda el límite muy pequeño como se ve aquí .

     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 diferencia aquí entre la respuesta anterior es que estaba usando utf8 en lugar de ut8mb4 y usando t1.data_type con t1.CHARACTER_MAXIMUM_LENGTH no funcionó para las enumeraciones. Además, mi consulta excluye las vistas, ya que tendrán que modificarse por separado.

Simplemente utilicé un script de Perl para devolver todos estos cambios como una matriz e iteré sobre ellos, arreglé las columnas que eran demasiado largas (generalmente eran varchar (256) cuando los datos generalmente solo tenían 20 caracteres en ellos, así que fue una solución fácil )

Encontré que algunos datos estaban dañados al alterar desde latin1 -> utf8mb4. Parecía que los caracteres latin1 codificados en utf8 en las columnas se burlarían en la conversión. Simplemente mantuve datos de las columnas que sabía que iban a ser un problema en la memoria antes y después de la modificación y los comparé y generé declaraciones de actualización para corregir los datos.

Jacob Hundley
fuente
4

Aquí describe bien el proceso. Sin embargo, algunos de los personajes que no encajaban en el espacio latino desaparecieron para siempre. UTF-8 es un SUPERSET de latin1. No al revés. La mayoría encajará en un espacio de un solo byte, pero los indefinidos no (verifique una lista de latin1; no todos los 256 caracteres están definidos, dependiendo de la definición de mysql latin1)

MJB
fuente