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.