MySQL tiene un juego de caracteres llamado utf8 que no es UTF-8. Guarda como mucho tres bytes por carácter, y todos los emojis y algunos caracteres poco frecuentes necesitan cuatro. Un texto que incluya uno se rechaza o se trunca en silencio justo ahí, y por eso un comentario puede desaparecer a partir de un párrafo.
Usa utf8mb4 en todas partes
ALTER DATABASE shop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
ALTER TABLE comments CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Y en la conexión, que es la mitad que se olvida
// PDO
new PDO('mysql:host=localhost;dbname=shop;charset=utf8mb4', $u, $p);
; my.cnf
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
Una tabla utf8mb4 leída por una conexión latin1 devuelve texto estropeado y vuelve a escribir texto estropeado. La tabla está bien, la conexión no, y los datos se corrompen por el camino. Configura las dos.
Qué cotejo
- utf8mb4_unicode_ci: ordena y compara según las reglas de Unicode entre idiomas. La opción segura por defecto.
- utf8mb4_0900_ai_ci: el valor por defecto de MySQL 8, Unicode más reciente, algo más rápido.
- utf8mb4_bin: bytes exactos. Úsalo para tokens y resúmenes, nunca para nombres.
El cotejo decide la comparación. Con un cotejo que ignora mayúsculas, WHERE name = 'ali' coincide con Ali: lo que suele querer una búsqueda, y no lo que quiere una clave de API.
Encuentra lo que sigue mal
SELECT table_name, table_collation
FROM information_schema.tables
WHERE table_schema = 'shop' AND table_collation NOT LIKE 'utf8mb4%';
SELECT table_name, column_name, character_set_name
FROM information_schema.columns
WHERE table_schema = 'shop' AND character_set_name IS NOT NULL
AND character_set_name <> 'utf8mb4';
Las columnas se convierten aparte de las tablas. Una tabla declarada utf8mb4 puede seguir albergando columnas latin1 de hace años, y son esas las que se rompen.
Haz una copia antes de convertir. Reescribe cada fila, puede tardar en una tabla grande y los límites de tamaño de los índices muerden de vez en cuando en versiones antiguas.
