LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

告别手动迁移!C#一键实现SQL Server到MySQL数据无缝转换

admin
2025年7月10日 15:13 本文热度 50

最近公司决定有一个项目从SQL Server迁移到MySQL以降低成本。面对200多张表的迁移任务,头疼了…,下次有时间改成ui的版本。

手动迁移表结构? 光是数据类型转换就能让人崩溃!

导出导入数据? 字符编码、主键冲突、数据格式不兼容…

在数据库迁移过程中都遇到过数据丢失或结构错误的问题。今天这篇文章,我将分享一套完整的C#自动化解决方案,让你轻松搞定数据库迁移!

🔍 问题分析:数据库迁移的三大痛点

😵 痛点1:数据类型映射复杂

SQL Server的nvarchar(MAX)对应MySQL的什么类型?datetime2又该如何转换?

😵 痛点2:表结构创建繁琐

手动写CREATE TABLE语句?一个表还好,几百个表简直是噩梦!

😵 痛点3:数据同步容易出错

字符转义、NULL值处理、大数据量传输…每一步都是坑!

🛠️ 解决方案:C#自动化迁移工具

🎯 核心设计思路

  1. 自动获取
    源表结构信息
  2. 智能映射
    数据类型
  3. 批量创建
    目标表
  4. 安全同步
    所有数据

💻 代码实战:完整解决方案

🔧 项目准备

首先安装必要的NuGet包:

System.Data.SqlClient
MySql.Data

🔥 核心转换器类

using System;
using System.Data;
using System.Data.SqlClient;
using MySql.Data.MySqlClient;
using System.Text;

namespace DatabaseSync
{
    publicclass DatabaseConverter
    {

        // 连接字符串存储
        privatestring sqlServerConnStr;
        privatestring mysqlConnStr;

        public DatabaseConverter(string sqlServerConnStr, string mysqlConnStr)
        
{
            this.sqlServerConnStr = sqlServerConnStr;
            this.mysqlConnStr = mysqlConnStr;
        }

        /// <summary>
        /// 一键转换主方法 - 这是整个流程的入口
        /// </summary>
        /// <param name="tableName">要转换的表名</param>
        public void Convert(string tableName)
        
{
            try
            {
                Console.WriteLine($"🚀 开始转换表:{tableName}");

                // 步骤1:获取SQL Server表结构
                DataTable schema = GetSqlServerTableSchema(tableName);
                Console.WriteLine("✅ 表结构获取完成");

                // 步骤2:创建MySQL表
                CreateMySqlTable(schema, tableName);
                Console.WriteLine("✅ MySQL表创建完成");

                // 步骤3:同步数据
                SyncData(tableName);
                Console.WriteLine($"🎉 表 {tableName} 转换完成!");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"❌ 转换过程出错:{ex.Message}");
                throw;
            }
        }
    }
}

📊 表结构获取方法

/// <summary>
/// 从SQL Server获取完整表结构信息
/// 包括:列名、数据类型、长度、是否允许NULL、是否自增等
/// </summary>
private DataTable GetSqlServerTableSchema(string tableName)
{
    using (SqlConnection conn = new SqlConnection(sqlServerConnStr))
    {
        conn.Open();

        // 🔍 关键SQL:获取表的完整结构信息
        string query = @"
            SELECT 
                COLUMN_NAME,                    -- 列名
                DATA_TYPE,                      -- 数据类型
                CHARACTER_MAXIMUM_LENGTH,       -- 最大长度
                IS_NULLABLE,                    -- 是否允许NULL
                COLUMN_DEFAULT,                 -- 默认值
                CASE WHEN COLUMNPROPERTY(OBJECT_ID(@TableName), COLUMN_NAME, 'IsIdentity') = 1 
                     THEN 'YES' 
                     ELSE 'NO' 
                END AS IS_IDENTITY              -- 是否自增
            FROM INFORMATION_SCHEMA.COLUMNS 
            WHERE TABLE_NAME = @TableName 
            ORDER BY ORDINAL_POSITION"
;

        using (SqlCommand cmd = new SqlCommand(query, conn))
        {
            // 🛡️ 使用参数化查询防止SQL注入
            cmd.Parameters.AddWithValue("@TableName", tableName);

            DataTable schema = new DataTable();
            schema.Load(cmd.ExecuteReader());
            return schema;
        }
    }
}

🏗️ MySQL表创建方法

/// <summary>
/// 根据SQL Server表结构创建对应的MySQL表
/// 自动处理数据类型映射和约束转换
/// </summary>
private void CreateMySqlTable(DataTable schema, string tableName)
{
    using (MySqlConnection conn = new MySqlConnection(mysqlConnStr))
    {
        conn.Open();

        StringBuilder createTableSql = new StringBuilder();
        createTableSql.AppendLine($"CREATE TABLE IF NOT EXISTS `{tableName}` (");

        // 🔄 遍历所有列,构建CREATE TABLE语句
        for (int i = 0; i < schema.Rows.Count; i++)
        {
            DataRow row = schema.Rows[i];
            string columnName = row["COLUMN_NAME"].ToString();
            string dataType = row["DATA_TYPE"].ToString();
            string maxLength = row["CHARACTER_MAXIMUM_LENGTH"].ToString();
            string isNullable = row["IS_NULLABLE"].ToString();
            string isIdentity = row["IS_IDENTITY"].ToString();

            // 构建列定义
            createTableSql.Append($"`{columnName}` {ConvertDataType(dataType, maxLength)}");

            // 🚀 处理自增属性
            if (isIdentity == "YES")
            {
                createTableSql.Append(" AUTO_INCREMENT");
            }

            // 🔒 处理NULL约束
            if (isNullable == "NO")
            {
                createTableSql.Append(" NOT NULL");
            }

            // 添加逗号分隔符(最后一列除外)
            if (i < schema.Rows.Count - 1)
            {
                createTableSql.AppendLine(",");
            }
        }

        // 🔑 添加主键(假设第一列为主键)
        createTableSql.AppendLine($",PRIMARY KEY (`{schema.Rows[0]["COLUMN_NAME"]}`)");
        createTableSql.AppendLine(") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;");

        using (MySqlCommand cmd = new MySqlCommand(createTableSql.ToString(), conn))
        {
            cmd.ExecuteNonQuery();
        }
    }
}

🔄 数据类型智能映射

/// <summary>
/// SQL Server数据类型到MySQL数据类型的智能映射
/// 这是迁移成功的关键!
/// </summary>
private string ConvertDataType(string sqlServerType, string maxLength, bool isPrimaryKey = false)
{
    switch (sqlServerType.ToLower())
    {
        // 🔢 整数类型映射
        case"int":
            return"int";
        case"bigint":
            return"bigint";
        case"smallint":
            return"smallint";
        case"tinyint":
            return"tinyint";
        case"bit":
            return"bit";

        // 💰 数值类型映射
        case"decimal":
        case"numeric":
            return"decimal(18,2)";
        case"float":
            return"float";

        // 📅 日期时间类型映射
        case"datetime":
        case"datetime2":
            return"datetime";
        case"date":
            return"date";
        case"time":
            return"time";

        // 📝 字符串类型映射
        case"char":
        case"nchar":
            return $"char({maxLength})";
        case"varchar":
        case"nvarchar":
            if (maxLength == "-1"// MAX类型
            {
                // 如果是主键,限制长度
                return isPrimaryKey ? "varchar(255)" : "text";
            }
            else
            {
                int length = int.Parse(maxLength);
                // 如果是主键且长度过大,限制为255
                if (isPrimaryKey && length > 255)
                {
                    return"varchar(255)";
                }
                return $"varchar({maxLength})";
            }
        case"text":
        case"ntext":
            // 如果是主键,使用varchar(255)
            return isPrimaryKey ? "varchar(255)" : "text";
        case"uniqueidentifier":
            return"varchar(36)";
        // 🔧 默认处理
        default:
            return isPrimaryKey ? "varchar(255)" : "text";
    }
}

📦 数据同步核心方法

/// <summary>
/// 核心数据同步方法 - 处理大数据量迁移
/// 包含字符转义、NULL值处理等关键逻辑
/// </summary>
private void SyncData(string tableName)
{
    using (SqlConnection sqlConn = new SqlConnection(sqlServerConnStr))
    using (MySqlConnection mysqlConn = new MySqlConnection(mysqlConnStr))
    {
        sqlConn.Open();
        mysqlConn.Open();

        // 📖 读取源数据
        using (SqlCommand sqlCmd = new SqlCommand($"SELECT * FROM {tableName}", sqlConn))
        using (SqlDataReader reader = sqlCmd.ExecuteReader())
        {
            DataTable schemaTable = reader.GetSchemaTable();

            if (reader.HasRows)
            {
                MySqlCommand mysqlCmd = new MySqlCommand();
                mysqlCmd.Connection = mysqlConn;

                int recordCount = 0;

                // 🔄 逐行处理数据
                while (reader.Read())
                {
                    StringBuilder insertSql = new StringBuilder();
                    insertSql.Append($"INSERT INTO `{tableName}` (");

                    // 🏷️ 构建列名部分
                    for (int i = 0; i < schemaTable.Rows.Count; i++)
                    {
                        string columnName = schemaTable.Rows[i]["ColumnName"].ToString();
                        insertSql.Append($"`{columnName}`");
                        if (i < schemaTable.Rows.Count - 1) insertSql.Append(",");
                    }

                    insertSql.Append(") VALUES (");

                    // 💾 构建值部分
                    for (int i = 0; i < reader.FieldCount; i++)
                    {
                        if (reader.IsDBNull(i))
                        {
                            insertSql.Append("NULL");
                        }
                        else
                        {
                            // 🔍 获取列的数据类型
                            string dataTypeName = schemaTable.Rows[i]["DataTypeName"].ToString();
                            object value = reader.GetValue(i);

                            // 📅 特殊处理日期时间类型
                            if (IsDateTimeType(dataTypeName))
                            {
                                DateTime dateTime = System.Convert.ToDateTime(value);
                                // 转换为MySQL标准格式:YYYY-MM-DD HH:mm:ss
                                string formattedDate = dateTime.ToString("yyyy-MM-dd HH:mm:ss");
                                insertSql.Append($"'{formattedDate}'");
                            }
                            else
                            {
                                if(dataTypeName == "bit")
                                {
                                    var bitValue = value.ToString()=="true" ? 1 : 0;
                                    insertSql.Append($"{bitValue}");
                                }
                                else
                                {
                                    string stringValue = value.ToString();
                                    // 🛡️ SQL注入防护 - 转义单引号
                                    insertSql.Append($"'{stringValue.Replace("'", "''")}'");
                                }

                            }
                        }

                        if (i < reader.FieldCount - 1) insertSql.Append("
,");
                    }

                    insertSql.Append("
)");

                    mysqlCmd.CommandText = insertSql.ToString();
                    mysqlCmd.ExecuteNonQuery();

                    recordCount++;

                    // 📊 进度提示
                    if (recordCount % 1000 == 0)
                    {
                        Console.WriteLine($"
已处理 {recordCount} 条记录...");
                    }
                }

                Console.WriteLine($"
✅ 数据同步完成,共处理 {recordCount} 条记录");
            }
        }
    }
}

/// <summary>
/// 判断是否为日期时间类型
/// </summary>
private bool IsDateTimeType(string dataTypeName)
{
    string[] dateTimeTypes = {
    "
DateTime", "DateTime2", "Date", "Time",
    "
SmallDateTime", "DateTimeOffset"
    };

    return dateTimeTypes.Contains(dataTypeName, StringComparer.OrdinalIgnoreCase);
}

🎯 使用示例

using System.Text;

namespace AppSqlserver2Mysql
{
    internal class Program
    {

        static void Main(string[] args)
        
{
            Console.InputEncoding = Encoding.UTF8;
            Console.OutputEncoding = Encoding.UTF8;
            try
            {
                // 🔗 配置连接字符串
                string sqlServerConnStr = "Server=.;Database=**;Trusted_Connection=True;";
                string mysqlConnStr = "Server=***;Database=test;Uid=root;Pwd=Iseeyou123;";

                // 🏭 创建转换器实例
                DatabaseConverter converter = new DatabaseConverter(sqlServerConnStr, mysqlConnStr);

                // 🚀 开始转换(可以批量处理多个表)
                string[] tablesToConvert = { "wms_user""wms_basic_material""wms_basic_location" };

                foreach (string tableName in tablesToConvert)
                {
                    converter.Convert(tableName);
                    Console.WriteLine($"✅ {tableName} 迁移完成!");
                }

                Console.WriteLine("🎉 所有表迁移完成!");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"❌ 程序执行失败:{ex.Message}");
            }
        }
    }
}

⚠️ 重要提醒:生产环境注意事项

🔒 1. 安全性考虑

  • 连接字符串加密
    生产环境中绝不要明文存储数据库密码
  • 权限最小化
    使用专门的迁移账户,只授予必要权限

🚀 2. 性能优化

  • 批量处理
    对于大表,建议分批次处理避免内存溢出
  • 事务控制
    添加事务确保数据一致性
// 💡 性能优化示例:批量插入
private void SyncDataInBatches(string tableName, int batchSize = 1000)
{
    // 使用MySQL的批量插入语法提升性能
    // INSERT INTO table VALUES (1,'a'),(2,'b'),(3,'c')...
}

📊 3. 监控与日志

  • 进度跟踪
    实时显示迁移进度
  • 错误记录
    详细记录失败的表和原因
  • 数据校验
    迁移完成后对比记录数确保完整性

✨ 总结:三个关键收获

🎯 1. 自动化是王道

手动迁移100张表?不如花2小时写个工具,让程序跑一夜!

🎯 2. 数据类型映射是核心

掌握SQL Server到MySQL的类型转换规则,这是迁移成功的关键。

🎯 3. 安全性不能忽视

字符转义、参数化查询、权限控制…每一个细节都关乎数据安全。上面例子其实最好参数化会好不少。


💬 互动时间

  1. 你们团队在数据库迁移时遇到过哪些坑?
  2. 除了SQL Server到MySQL,还需要其他数据库的迁移方案吗?

觉得这篇文章对你有帮助的话,记得转发给更多需要的同行!让我们一起告别手动迁移的痛苦时代!🚀


阅读原文:https://mp.weixin.qq.com/s/ITJZXvzX3rwdn6t1Q6iyLQ


该文章在 2025/7/10 15:31:26 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved