SQL
是否有PHP
我可以运行的脚本来更改数据库中所有表和字段中的默认排序规则?
我可以自己写一个,但我认为这应该是在这样的网站上很容易获得的东西。如果我能在有人发布之前自己想出一个,我会自己发布。
可以在单个命令中完成(而不是 PHP 的 148 个):
mysql --database=dbname -B -N -e "SHOW TABLES" \
| awk '{print "SET foreign_key_checks = 0; ALTER TABLE", $1, "CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; SET foreign_key_checks = 1; "}' \
| mysql --database=dbname &
您必须喜欢命令行...(您可能需要使用--user
和--password
选项mysql
)。
编辑:为了避免外键问题,添加SET foreign_key_checks = 0;
和SET foreign_key_checks = 1;
我认为在运行 PhpMyAdmin 的两个步骤中很容易做到这一点。
第1步:
SELECT CONCAT('ALTER TABLE `', t.`TABLE_SCHEMA`, '`.`', t.`TABLE_NAME`,
'` CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;') as stmt
FROM `information_schema`.`TABLES` t
WHERE 1
AND t.`TABLE_SCHEMA` = 'database_name'
ORDER BY 1
第 2 步:
此查询将输出一个查询列表,每个表一个。您必须复制查询列表,并将它们粘贴到命令行或 PhpMyAdmin 的 SQL 选项卡以进行更改。
好的,我写这个是考虑到这个线程中所说的。感谢您的帮助,我希望这个脚本能帮助其他人。我对其使用没有任何保证,因此请在运行前备份。它应该适用于所有数据库;它对我自己很好。
编辑:在顶部添加了要转换为的字符集/整理的变量。EDIT2:更改数据库和表的默认字符集/整理
<?php
function MysqlError()
{
if (mysql_errno())
{
echo "<b>Mysql Error: " . mysql_error() . "</b>\n";
}
}
$username = "root";
$password = "";
$db = "database";
$host = "localhost";
$target_charset = "utf8";
$target_collate = "utf8_general_ci";
echo "<pre>";
$conn = mysql_connect($host, $username, $password);
mysql_select_db($db, $conn);
$tabs = array();
$res = mysql_query("SHOW TABLES");
MysqlError();
while (($row = mysql_fetch_row($res)) != null)
{
$tabs[] = $row[0];
}
// now, fix tables
foreach ($tabs as $tab)
{
$res = mysql_query("show index from {$tab}");
MysqlError();
$indicies = array();
while (($row = mysql_fetch_array($res)) != null)
{
if ($row[2] != "PRIMARY")
{
$indicies[] = array("name" => $row[2], "unique" => !($row[1] == "1"), "col" => $row[4]);
mysql_query("ALTER TABLE {$tab} DROP INDEX {$row[2]}");
MysqlError();
echo "Dropped index {$row[2]}. Unique: {$row[1]}\n";
}
}
$res = mysql_query("DESCRIBE {$tab}");
MysqlError();
while (($row = mysql_fetch_array($res)) != null)
{
$name = $row[0];
$type = $row[1];
$set = false;
if (preg_match("/^varchar\((\d+)\)$/i", $type, $mat))
{
$size = $mat[1];
mysql_query("ALTER TABLE {$tab} MODIFY {$name} VARBINARY({$size})");
MysqlError();
mysql_query("ALTER TABLE {$tab} MODIFY {$name} VARCHAR({$size}) CHARACTER SET {$target_charset}");
MysqlError();
$set = true;
echo "Altered field {$name} on {$tab} from type {$type}\n";
}
else if (!strcasecmp($type, "CHAR"))
{
mysql_query("ALTER TABLE {$tab} MODIFY {$name} BINARY(1)");
MysqlError();
mysql_query("ALTER TABLE {$tab} MODIFY {$name} VARCHAR(1) CHARACTER SET {$target_charset}");
MysqlError();
$set = true;
echo "Altered field {$name} on {$tab} from type {$type}\n";
}
else if (!strcasecmp($type, "TINYTEXT"))
{
mysql_query("ALTER TABLE {$tab} MODIFY {$name} TINYBLOB");
MysqlError();
mysql_query("ALTER TABLE {$tab} MODIFY {$name} TINYTEXT CHARACTER SET {$target_charset}");
MysqlError();
$set = true;
echo "Altered field {$name} on {$tab} from type {$type}\n";
}
else if (!strcasecmp($type, "MEDIUMTEXT"))
{
mysql_query("ALTER TABLE {$tab} MODIFY {$name} MEDIUMBLOB");
MysqlError();
mysql_query("ALTER TABLE {$tab} MODIFY {$name} MEDIUMTEXT CHARACTER SET {$target_charset}");
MysqlError();
$set = true;
echo "Altered field {$name} on {$tab} from type {$type}\n";
}
else if (!strcasecmp($type, "LONGTEXT"))
{
mysql_query("ALTER TABLE {$tab} MODIFY {$name} LONGBLOB");
MysqlError();
mysql_query("ALTER TABLE {$tab} MODIFY {$name} LONGTEXT CHARACTER SET {$target_charset}");
MysqlError();
$set = true;
echo "Altered field {$name} on {$tab} from type {$type}\n";
}
else if (!strcasecmp($type, "TEXT"))
{
mysql_query("ALTER TABLE {$tab} MODIFY {$name} BLOB");
MysqlError();
mysql_query("ALTER TABLE {$tab} MODIFY {$name} TEXT CHARACTER SET {$target_charset}");
MysqlError();
$set = true;
echo "Altered field {$name} on {$tab} from type {$type}\n";
}
if ($set)
mysql_query("ALTER TABLE {$tab} MODIFY {$name} COLLATE {$target_collate}");
}
// re-build indicies..
foreach ($indicies as $index)
{
if ($index["unique"])
{
mysql_query("CREATE UNIQUE INDEX {$index["name"]} ON {$tab} ({$index["col"]})");
MysqlError();
}
else
{
mysql_query("CREATE INDEX {$index["name"]} ON {$tab} ({$index["col"]})");
MysqlError();
}
echo "Created index {$index["name"]} on {$tab}. Unique: {$index["unique"]}\n";
}
// set default collate
mysql_query("ALTER TABLE {$tab} DEFAULT CHARACTER SET {$target_charset} COLLATE {$target_collate}");
}
// set database charset
mysql_query("ALTER DATABASE {$db} DEFAULT CHARACTER SET {$target_charset} COLLATE {$target_collate}");
mysql_close($conn);
echo "</pre>";
?>
当心!如果您实际上将 utf 存储为另一种编码,那么您的手上可能会一团糟。先备份。然后尝试一些标准方法:
例如 http://www.cesspit.net/drupal/node/898 http://www.hackszine.com/blog/archive/2007/05/mysql_database_migration_latin.html
我不得不求助于将所有文本字段转换为二进制,然后再转换回 varchar/text。这救了我的屁股。
我的数据是 UTF8,存储为 latin1。我做了什么:
删除索引。将字段转换为二进制。转换为 utf8 通用 ci
如果您在 LAMP 上,请不要忘记在与数据库交互之前添加 set NAMES 命令,并确保设置字符编码标头。
此 PHP 片段将更改数据库中所有表的排序规则。(取自本站。)
<?php
// your connection
mysql_connect("localhost","root","***");
mysql_select_db("db1");
// convert code
$res = mysql_query("SHOW TABLES");
while ($row = mysql_fetch_array($res))
{
foreach ($row as $key => $table)
{
mysql_query("ALTER TABLE " . $table . " CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci");
echo $key . " => " . $table . " CONVERTED<br />";
}
}
?>
另一种使用命令行的方法,基于@david,没有awk
for t in $(mysql --user=root --password=admin --database=DBNAME -e "show tables";);do echo "Altering" $t;mysql --user=root --password=admin --database=DBNAME -e "ALTER TABLE $t CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;";done
美化了
for t in $(mysql --user=root --password=admin --database=DBNAME -e "show tables";);
do
echo "Altering" $t;
mysql --user=root --password=admin --database=DBNAME -e "ALTER TABLE $t CONVERT TO CHARACTER SET utf8 COLLATE utf8_unicode_ci;";
done
可以在此处找到上述脚本的更完整版本:
http://www.zen-cart.com/index.php?main_page=product_contrib_info&products_id=1937
请在此处留下有关此贡献的任何反馈:http ://www.zen-cart.com/forum/showthread.php?p=1034214
字符集和排序规则不是一回事。排序规则是一组关于如何对字符串进行排序的规则。字符集是一组关于如何表示字符的规则。排序规则取决于字符集。
在所有选择转换的表格上方的脚本中(使用SHOW TABLES
),但这是一种在转换表格之前检查表格排序规则的更方便和可移植的方式。这个查询做到了:
SELECT table_name
, table_collation
FROM information_schema.tables
使用我的自定义外壳collatedb,它应该可以工作:
collatedb <username> <password> <database> <collation>
例子 :
collatedb root 0000 myDatabase utf8_bin
感谢@nlaq 的代码,这让我开始使用以下解决方案。
我发布了一个 WordPress 插件,却没有意识到 WordPress 不会自动设置排序规则。所以很多使用插件的人最终latin1_swedish_ci
都得到了应该是utf8_general_ci
.
这是我添加到插件中的代码,用于检测latin1_swedish_ci
整理并将其更改为utf8_general_ci
.
在您自己的插件中使用之前测试此代码!
// list the names of your wordpress plugin database tables (without db prefix)
$tables_to_check = array(
'social_message',
'social_facebook',
'social_facebook_message',
'social_facebook_page',
'social_google',
'social_google_mesage',
'social_twitter',
'social_twitter_message',
);
// choose the collate to search for and replace:
$convert_fields_collate_from = 'latin1_swedish_ci';
$convert_fields_collate_to = 'utf8_general_ci';
$convert_tables_character_set_to = 'utf8';
$show_debug_messages = false;
global $wpdb;
$wpdb->show_errors();
foreach($tables_to_check as $table) {
$table = $wpdb->prefix . $table;
$indicies = $wpdb->get_results( "SHOW INDEX FROM `$table`", ARRAY_A );
$results = $wpdb->get_results( "SHOW FULL COLUMNS FROM `$table`" , ARRAY_A );
foreach($results as $result){
if($show_debug_messages)echo "Checking field ".$result['Field'] ." with collat: ".$result['Collation']."\n";
if(isset($result['Field']) && $result['Field'] && isset($result['Collation']) && $result['Collation'] == $convert_fields_collate_from){
if($show_debug_messages)echo "Table: $table - Converting field " .$result['Field'] ." - " .$result['Type']." - from $convert_fields_collate_from to $convert_fields_collate_to \n";
// found a field to convert. check if there's an index on this field.
// we have to remove index before converting field to binary.
$is_there_an_index = false;
foreach($indicies as $index){
if ( isset($index['Column_name']) && $index['Column_name'] == $result['Field']){
// there's an index on this column! store it for adding later on.
$is_there_an_index = $index;
$wpdb->query( $wpdb->prepare( "ALTER TABLE `%s` DROP INDEX %s", $table, $index['Key_name']) );
if($show_debug_messages)echo "Dropped index ".$index['Key_name']." before converting field.. \n";
break;
}
}
$set = false;
if ( preg_match( "/^varchar\((\d+)\)$/i", $result['Type'], $mat ) ) {
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` VARBINARY({$mat[1]})" );
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` VARCHAR({$mat[1]}) CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
$set = true;
} else if ( !strcasecmp( $result['Type'], "CHAR" ) ) {
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` BINARY(1)" );
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` VARCHAR(1) CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
$set = true;
} else if ( !strcasecmp( $result['Type'], "TINYTEXT" ) ) {
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` TINYBLOB" );
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` TINYTEXT CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
$set = true;
} else if ( !strcasecmp( $result['Type'], "MEDIUMTEXT" ) ) {
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` MEDIUMBLOB" );
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` MEDIUMTEXT CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
$set = true;
} else if ( !strcasecmp( $result['Type'], "LONGTEXT" ) ) {
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` LONGBLOB" );
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` LONGTEXT CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
$set = true;
} else if ( !strcasecmp( $result['Type'], "TEXT" ) ) {
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` BLOB" );
$wpdb->query( "ALTER TABLE `{$table}` MODIFY `{$result['Field']}` TEXT CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
$set = true;
}else{
if($show_debug_messages)echo "Failed to change field - unsupported type: ".$result['Type']."\n";
}
if($set){
if($show_debug_messages)echo "Altered field success! \n";
$wpdb->query( "ALTER TABLE `$table` MODIFY {$result['Field']} COLLATE $convert_fields_collate_to" );
}
if($is_there_an_index !== false){
// add the index back.
if ( !$is_there_an_index["Non_unique"] ) {
$wpdb->query( "CREATE UNIQUE INDEX `{$is_there_an_index['Key_name']}` ON `{$table}` ({$is_there_an_index['Column_name']})", $is_there_an_index['Key_name'], $table, $is_there_an_index['Column_name'] );
} else {
$wpdb->query( "CREATE UNIQUE INDEX `{$is_there_an_index['Key_name']}` ON `{$table}` ({$is_there_an_index['Column_name']})", $is_there_an_index['Key_name'], $table, $is_there_an_index['Column_name'] );
}
}
}
}
// set default collate
$wpdb->query( "ALTER TABLE `{$table}` DEFAULT CHARACTER SET {$convert_tables_character_set_to} COLLATE {$convert_fields_collate_to}" );
if($show_debug_messages)echo "Finished with table $table \n";
}
$wpdb->hide_errors();
一个简单的(愚蠢的?:) 解决方案,使用您的 IDE 的多选功能:
我认为最快的方法是使用 phpmyadmin 和控制台上的一些 jQuery。
转到表的结构并打开 chrome/firefox 开发人员控制台(通常在键盘上按 F12):
运行此代码以选择所有字符集不正确的字段并开始修改:
var elems = $('dfn'); var lastID = elems.length - 1;
elems.each(function(i) {
if ($(this).html() != 'utf8_general_ci') {
$('input:checkbox', $('td', $(this).parent().parent()).first()).attr('checked','checked');
}
if (i == lastID) {
$("button[name='submit_mult'][value='change']").click();
}
});
加载页面时,在控制台上使用此代码选择正确的编码:
$("select[name*='field_collation']" ).val('utf8_general_ci');
节省
在“操作”选项卡上的“排序规则”字段中更改表格的字符集
在 phpmyadmin 4.0 和 4.4 上测试,但我认为适用于所有 4.x 版本
如果您没有命令行访问权限或编辑 INFORMATION_SCHEMA 的访问权限,这是一个简单的方法,只需 phpmyadmin。
首先,听听这里许多其他答案的建议——你真的可以把事情搞砸,所以做一个备份。现在备份您的备份。此外,如果您的数据编码不同于您将其更改为的编码,这也不太可能起作用。
请注意,您需要在开始之前找到需要更改的违规模式和字符编码的确切名称。
这是一种超级简单的方法,但同样,这不会改变数据的编码,所以它只在某些情况下有效。
我更新了 nlaq 的答案以使用 PHP7 并正确处理多列索引、二进制整理数据(例如latin1_bin
)等,并稍微清理了代码。这是我发现/尝试的唯一将我的数据库从 latin1 成功迁移到 utf8 的代码。
<?php
/////////// BEGIN CONFIG ////////////////////
$username = "";
$password = "";
$db = "";
$host = "";
$target_charset = "utf8";
$target_collation = "utf8_unicode_ci";
$target_bin_collation = "utf8_bin";
/////////// END CONFIG ////////////////////
function MySQLSafeQuery($conn, $query) {
$res = mysqli_query($conn, $query);
if (mysqli_errno($conn)) {
echo "<b>Mysql Error: " . mysqli_error($conn) . "</b>\n";
echo "<span>This query caused the above error: <i>" . $query . "</i></span>\n";
}
return $res;
}
function binary_typename($type) {
$mysql_type_to_binary_type_map = array(
"VARCHAR" => "VARBINARY",
"CHAR" => "BINARY(1)",
"TINYTEXT" => "TINYBLOB",
"MEDIUMTEXT" => "MEDIUMBLOB",
"LONGTEXT" => "LONGBLOB",
"TEXT" => "BLOB"
);
$typename = "";
if (preg_match("/^varchar\((\d+)\)$/i", $type, $mat))
$typename = $mysql_type_to_binary_type_map["VARCHAR"] . "(" . (2*$mat[1]) . ")";
else if (!strcasecmp($type, "CHAR"))
$typename = $mysql_type_to_binary_type_map["CHAR"] . "(1)";
else if (array_key_exists(strtoupper($type), $mysql_type_to_binary_type_map))
$typename = $mysql_type_to_binary_type_map[strtoupper($type)];
return $typename;
}
echo "<pre>";
// Connect to database
$conn = mysqli_connect($host, $username, $password);
mysqli_select_db($conn, $db);
// Get list of tables
$tabs = array();
$query = "SHOW TABLES";
$res = MySQLSafeQuery($conn, $query);
while (($row = mysqli_fetch_row($res)) != null)
$tabs[] = $row[0];
// Now fix tables
foreach ($tabs as $tab) {
$res = MySQLSafeQuery($conn, "SHOW INDEX FROM `{$tab}`");
$indicies = array();
while (($row = mysqli_fetch_array($res)) != null) {
if ($row[2] != "PRIMARY") {
$append = true;
foreach ($indicies as $index) {
if ($index["name"] == $row[2]) {
$index["col"][] = $row[4];
$append = false;
}
}
if($append)
$indicies[] = array("name" => $row[2], "unique" => !($row[1] == "1"), "col" => array($row[4]));
}
}
foreach ($indicies as $index) {
MySQLSafeQuery($conn, "ALTER TABLE `{$tab}` DROP INDEX `{$index["name"]}`");
echo "Dropped index {$index["name"]}. Unique: {$index["unique"]}\n";
}
$res = MySQLSafeQuery($conn, "SHOW FULL COLUMNS FROM `{$tab}`");
while (($row = mysqli_fetch_array($res)) != null) {
$name = $row[0];
$type = $row[1];
$current_collation = $row[2];
$target_collation_bak = $target_collation;
if(!strcasecmp($current_collation, "latin1_bin"))
$target_collation = $target_bin_collation;
$set = false;
$binary_typename = binary_typename($type);
if ($binary_typename != "") {
MySQLSafeQuery($conn, "ALTER TABLE `{$tab}` MODIFY `{$name}` {$binary_typename}");
MySQLSafeQuery($conn, "ALTER TABLE `{$tab}` MODIFY `{$name}` {$type} CHARACTER SET '{$target_charset}' COLLATE '{$target_collation}'");
$set = true;
echo "Altered field {$name} on {$tab} from type {$type}\n";
}
$target_collation = $target_collation_bak;
}
// Rebuild indicies
foreach ($indicies as $index) {
// Handle multi-column indices
$joined_col_str = "";
foreach ($index["col"] as $col)
$joined_col_str = $joined_col_str . ", `" . $col . "`";
$joined_col_str = substr($joined_col_str, 2);
$query = "";
if ($index["unique"])
$query = "CREATE UNIQUE INDEX `{$index["name"]}` ON `{$tab}` ({$joined_col_str})";
else
$query = "CREATE INDEX `{$index["name"]}` ON `{$tab}` ({$joined_col_str})";
MySQLSafeQuery($conn, $query);
echo "Created index {$index["name"]} on {$tab}. Unique: {$index["unique"]}\n";
}
// Set default character set and collation for table
MySQLSafeQuery($conn, "ALTER TABLE `{$tab}` DEFAULT CHARACTER SET '{$target_charset}' COLLATE '{$target_collation}'");
}
// Set default character set and collation for database
MySQLSafeQuery($conn, "ALTER DATABASE `{$db}` DEFAULT CHARACTER SET '{$target_charset}' COLLATE '{$target_collation}'");
mysqli_close($conn);
echo "</pre>";
?>
除了@davidwinterbottom 答案,Windows 用户还可以使用以下命令:
mysql.exe --database=[database] -u [user] -p[password] -B -N -e "SHOW TABLES" \
| awk.exe '{print "SET foreign_key_checks = 0; ALTER TABLE", $1, "CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci; SET foreign_key_checks = 1; "}' \
| mysql.exe -u [user] -p[password] --database=[database] &
将 [database]、[user] 和 [password] 占位符替换为实际值。
Git-bash用户可以下载这个bash 脚本并轻松运行它。
对于它的价值,这里是https://stackoverflow.com/a/42545503/6226915作为 Yii2 ConsoleController 类的扩展版本,它也可以独立使用:https ://gist.github.com/cboulanger/d30c197235a53d9a2331f19a96d6e00d 。
该脚本修复了一些错误并添加了对全文索引的支持;它还处理 3kb 的常规索引键长度约束。它还将所有表转换为 InnoDB。
对于 PHP Laravel 框架:
使用迁移:php artisan make:migration update_character_set_utf8_m4
迁移文件逻辑
$DBNAME = config('database.connections.mysql.database');
$CHARACTER = 'utf8mb4';
$COLLATE = 'utf8mb4_unicode_ci';
echo "Altering DB $DBNAME\n";
DB::unprepared("ALTER DATABASE $DBNAME CHARACTER SET $CHARACTER COLLATE $COLLATE;");
$tables = DB::select("SELECT table_name FROM information_schema.tables WHERE table_schema = '{$DBNAME}'");
foreach ($tables as $table) {
echo "Altering $table->table_name\n";
DB::unprepared("ALTER TABLE $table->table_name CONVERT TO CHARACTER SET $CHARACTER COLLATE $COLLATE;");
}
php artisan migrate