C#程序实现将MySQL的存储过程转换为Azure Synapse Dedicated SQL Pool的T-SQL存储过程

C# 项目代码

📁 项目结构(.NET 6 / 7 / 8 控制台应用):

复制代码
MySqlToTSqlMigrator/
├── Program.cs                  // 主程序:输入 MySQL 代码,输出 T-SQL
├── MySqlParser.cs              // 增强版解析器:函数、控制流块、DML、赋值、类型等
├── TSqlGenerator.cs            // 生成器:将解析块拼接为完整 T-SQL,带注释
├── TypeMapper.cs               // MySQL → T-SQL 数据类型映射
├── FunctionMapper.cs           // MySQL 函数 → T-SQL 函数
├── ControlFlowConverter.cs     // 控制流语句转换(REPEAT → WHILE 等)
└── Models/
    ├── CodeBlock.cs            // 代码块类型定义
    └── VariableDeclaration.cs  // (备用,可扩展)

1️⃣ Program.cs(主程序入口 --- 无变化,直接复用)

csharp 复制代码
using System;

namespace MySqlToTSqlMigrator
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("=== MySQL 存储过程 → Azure Synapse T-SQL 转换工具 ===");
            Console.WriteLine("请输入您的 MySQL 存储过程代码(以单独一行输入 'END' 结束):");

            string input = "";
            string line;
            while (!(line = Console.ReadLine())?.Trim().ToUpper() == "END")
            {
                input += line + Environment.NewLine;
            }

            if (string.IsNullOrWhiteSpace(input))
            {
                Console.WriteLine("未输入有效的 MySQL 存储过程代码。");
                return;
            }

            try
            {
                var parser = new MySqlParser();
                var blocks = parser.Parse(input);

                var generator = new TSqlGenerator();
                string tsqlOutput = generator.Generate(blocks);

                Console.WriteLine("\n===== 转换后的 T-SQL 存储过程(带注释) =====");
                Console.WriteLine(tsqlOutput);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"转换出错: {ex.Message}");
            }
        }
    }
}

2️⃣ MySqlParser.cs(增强版解析器 --- 核心增强逻辑)

🔧 文件:MySqlParser.cs

csharp 复制代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using MySqlToTSqlMigrator.Models;

namespace MySqlToTSqlMigrator
{
    public class MySqlParser
    {
        private readonly TypeMapper _typeMapper = new TypeMapper();
        private readonly FunctionMapper _functionMapper = new FunctionMapper();
        private readonly ControlFlowConverter _controlFlowConverter = new ControlFlowConverter();

        public List<CodeBlock> Parse(string mysqlCode)
        {
            var blocks = new List<CodeBlock>();
            string[] lines = mysqlCode.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None);

            for (int i = 0; i < lines.Length; i++)
            {
                string line = lines[i].Trim();
                if (string.IsNullOrWhiteSpace(line) || line.StartsWith("--") || line.StartsWith("/*"))
                    continue;

                string original = line;
                string converted = original;
                string comment = "";

                // ===== 1. 解析 CREATE PROCEDURE =====
                if (line.StartsWith("CREATE PROCEDURE", StringComparison.OrdinalIgnoreCase))
                {
                    var (tsql, c) = ProcessProcedureHeader(line);
                    blocks.Add(new CodeBlock(BlockType.ProcedureHeader, original, tsql, c));
                    continue;
                }

                // ===== 2. 解析 DECLARE 变量(带类型映射)=====
                if (line.StartsWith("DECLARE", StringComparison.OrdinalIgnoreCase))
                {
                    var (tsql, c) = ExtractAndMapVariableDeclaration(line);
                    blocks.Add(new CodeBlock(BlockType.VariableDeclaration, original, tsql, c));
                    continue;
                }

                // ===== 3. 解析函数调用:IFNULL(x,y), NOW(), CONCAT() =====
                if (RegexSearchFunctions(line, out string func, out string mappedFunc, out string funcComment))
                {
                    converted = line.Replace(func, mappedFunc);
                    comment = funcComment;
                    blocks.Add(new CodeBlock(BlockType.Other, original, converted, comment));
                    continue;
                }

                // ===== 4. 解析 DML 中的 LIMIT 并移除 =====
                if (RemoveLimitFromDml(line, out converted, out comment))
                {
                    blocks.Add(new CodeBlock(BlockType.DmlStatement, original, converted, comment));
                    continue;
                }

                // ===== 5. 解析变量赋值:SET x=1, SELECT val INTO x =====
                if (ParseVariableAssignment(line, out converted, out comment))
                {
                    blocks.Add(new CodeBlock(BlockType.Other, original, converted, comment));
                    continue;
                }

                // ===== 6. 解析控制流关键字(REPEAT, LEAVE, ITERATE)=====
                if (line.StartsWith("REPEAT", StringComparison.OrdinalIgnoreCase))
                {
                    var (tsql, c) = _controlFlowConverter.Convert("REPEAT");
                    blocks.Add(new CodeBlock(BlockType.ControlFlow, original, tsql, c));
                    continue;
                }
                if (line.StartsWith("LEAVE", StringComparison.OrdinalIgnoreCase))
                {
                    var (tsql, c) = _controlFlowConverter.Convert("LEAVE");
                    blocks.Add(new CodeBlock(BlockType.ControlFlow, original, tsql, c));
                    continue;
                }
                if (line.StartsWith("ITERATE", StringComparison.OrdinalIgnoreCase))
                {
                    var (tsql, c) = _controlFlowConverter.Convert("ITERATE");
                    blocks.Add(new CodeBlock(BlockType.ControlFlow, original, tsql, c));
                    continue;
                }

                // ===== 7. 游标、异常、事务(简单注释)=====
                if (line.StartsWith("DECLARE CURSOR", StringComparison.OrdinalIgnoreCase))
                {
                    comment = " -- [转换说明] DECLARE CURSOR → 建议改用集合操作(如 INSERT...SELECT)";
                    blocks.Add(new CodeBlock(BlockType.CursorOperation, original, original, comment));
                    continue;
                }
                if (line.StartsWith("DECLARE HANDLER", StringComparison.OrdinalIgnoreCase))
                {
                    comment = " -- [转换说明] DECLARE HANDLER → 建议转为 TRY...CATCH(未实现)";
                    blocks.Add(new CodeBlock(BlockType.ExceptionHandler, original, original, comment));
                    continue;
                }
                if (line.StartsWith("START TRANSACTION") || line.StartsWith("BEGIN") || line.StartsWith("COMMIT") || line.StartsWith("ROLLBACK"))
                {
                    comment = " -- [转换说明] 事务控制 → Synapse 支持但有限制";
                    blocks.Add(new CodeBlock(BlockType.Transaction, original, original, comment));
                    continue;
                }

                // ===== 8. 其它未解析的语句 =====
                blocks.Add(new CodeBlock(BlockType.Other, original, original, " -- ⚠️ 未解析,建议手动转换"));
            }

            return blocks;
        }

        private (string, string) ProcessProcedureHeader(string line)
        {
            return (line, " -- [解析] 存储过程头部,需后续解析参数");
        }

        private (string, string) ExtractAndMapVariableDeclaration(string line)
        {
            // 简单实现:只提取类型并映射,如 DECLARE x INT → DECLARE @x INT
            var typeMatch = Regex.Match(line, @"\b(INT|VARCHAR|TEXT|DATETIME|ENUM)\b", RegexOptions.IgnoreCase);
            if (typeMatch.Success)
            {
                var mysqlType = typeMatch.Value;
                var (tsqlType, comment) = _typeMapper.Map(mysqlType);
                var replaced = line.Replace(mysqlType, tsqlType);
                return (replaced, comment);
            }
            return (line, " -- [解析] DECLARE 变量(类型未映射)");
        }

        private bool RegexSearchFunctions(string line, out string func, out string mappedFunc, out string comment)
        {
            func = ""; mappedFunc = ""; comment = "";
            var matches = Regex.Matches(line, @"(IFNULL|NOW|CONCAT)\(", RegexOptions.IgnoreCase);
            if (matches.Count > 0)
            {
                func = matches[0].Groups[1].Value;
                var mapped = _functionMapper.Map(func);
                mappedFunc = mapped.TSqlFunction;
                comment = mapped.Comment;
                return true;
            }
            return false;
        }

        private bool RemoveLimitFromDml(string line, out string converted, out string comment)
        {
            converted = line;
            comment = "";
            if (Regex.IsMatch(line, @"\b(LIMIT)\b", RegexOptions.IgnoreCase))
            {
                converted = Regex.Replace(line, @"\s+LIMIT\s+[\d,]+\s*", " ", RegexOptions.IgnoreCase);
                comment = " -- [转换说明] MySQL LIMIT 已移除(T-SQL 不支持)";
                return true;
            }
            return false;
        }

        private bool ParseVariableAssignment(string line, out string converted, out string comment)
        {
            converted = line;
            comment = "";
            if (line.StartsWith("SET ") || line.StartsWith("SELECT "))
            {
                // 简单保留,可后续解析 SET x=1 或 SELECT col INTO x
                comment = " -- [解析] 变量赋值语句(SET / SELECT),可进一步解析";
                return true;
            }
            comment = " -- [解析] 非赋值语句";
            return false;
        }
    }
}

3️⃣ TSqlGenerator.cs(生成最终带注释的 T-SQL)

🔧 文件:TSqlGenerator.cs

csharp 复制代码
using System.Text;
using MySqlToTSqlMigrator.Models;

namespace MySqlToTSqlMigrator
{
    public class TSqlGenerator
    {
        public string Generate(List<CodeBlock> blocks)
        {
            var sb = new StringBuilder();
            foreach (var block in blocks)
            {
                sb.AppendLine(block.ConvertedText);
                if (!string.IsNullOrEmpty(block.Comment))
                    sb.AppendLine(block.Comment);
                sb.AppendLine();
            }
            return sb.ToString();
        }
    }
}

🧩 TypeMapper(MySQL → T-SQL 数据类型映射)

功能:

  • 接收一个 MySQL 数据类型字符串(如 "INT", "VARCHAR(255)", "DATETIME", "TEXT", "ENUM('A','B')", "BLOB"
  • 返回对应的 T-SQL(Synapse 兼容)数据类型,并附带注释说明(如需要)

✅ 代码实现:TypeMapper.cs

csharp 复制代码
using System;
using System.Collections.Generic;

namespace MySqlToTSqlMigrator
{
    public static class TypeMapper
    {
        private static readonly Dictionary<string, string> TypeMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
        {
            // 数值类型
            { "TINYINT", "TINYINT" },
            { "SMALLINT", "SMALLINT" },
            { "MEDIUMINT", "INT" },           // Synapse 无 MEDIUMINT,映射为 INT
            { "INT", "INT" },
            { "INTEGER", "INT" },
            { "BIGINT", "BIGINT" },
            { "FLOAT", "FLOAT" },
            { "DOUBLE", "FLOAT(53)" },        // 或 DOUBLE PRECISION
            { "DECIMAL", "DECIMAL" },
            { "NUMERIC", "DECIMAL" },

            // 字符串类型
            { "CHAR", "CHAR" },
            { "VARCHAR", "VARCHAR" },
            { "TEXT", "VARCHAR(MAX)" },       // MySQL TEXT → T-SQL VARCHAR(MAX)
            { "TINYTEXT", "VARCHAR(255)" },
            { "MEDIUMTEXT", "VARCHAR(MAX)" },
            { "LONGTEXT", "VARCHAR(MAX)" },
            { "ENUM", "VARCHAR(100)" },       // ENUM 不直接支持,建议用 VARCHAR + 检查约束
            { "SET", "VARCHAR(MAX)" },        // SET 也不支持,用 VARCHAR

            // 二进制
            { "BLOB", "VARBINARY(MAX)" },
            { "TINYBLOB", "VARBINARY(255)" },
            { "MEDIUMBLOB", "VARBINARY(MAX)" },
            { "LONGBLOB", "VARBINARY(MAX)" },

            // 日期时间
            { "DATE", "DATE" },
            { "DATETIME", "DATETIME2" },      // MySQL DATETIME → T-SQL DATETIME2
            { "TIMESTAMP", "DATETIME2" },
            { "TIME", "TIME" },
            { "YEAR", "INT" }                 // YEAR(4) 一般存为 INT

            // 布尔(MySQL 无原生 BOOLEAN,常用 TINYINT(1) 或 ENUM)
            { "BOOLEAN", "BIT" }              // T-SQL 用 BIT 表示布尔
        };

        /// <summary>
        /// 将 MySQL 数据类型映射为 T-SQL 类型,返回映射结果和可选注释
        /// </summary>
        /// <param name="mysqlType">如 "VARCHAR(255)", "INT", "TEXT"</param>
        /// <returns>Tuple<TSqlType, Comment></returns>
        public static (string TSqlType, string Comment) Map(string mysqlType)
        {
            if (string.IsNullOrWhiteSpace(mysqlType))
                return ("VARCHAR(255)", "⚠️ 未识别的 MySQL 类型,已默认映射为 VARCHAR(255)");

            // 尝试精确匹配(如 VARCHAR(255) → 提取 VARCHAR)
            string baseType = mysqlType.Split('(')[0].Trim().ToUpperInvariant();

            if (TypeMap.TryGetValue(baseType, out string tsqlType))
            {
                string comment = TypeMap.ContainsKey(baseType) && (baseType == "ENUM" || baseType == "SET" || baseType == "TEXT" || baseType == "BLOB")
                    ? $" -- [映射说明] MySQL {baseType} 类型在 T-SQL 中无直接等价,已转换为 {tsqlType}"
                    : "";

                // 保留原类型中的长度定义,如 VARCHAR(255) → VARCHAR(255)
                if (mysqlType.Contains("("))
                {
                    int idx = mysqlType.IndexOf('(');
                    string args = mysqlType.Substring(idx);
                    tsqlType = tsqlType.Split('(')[0] + args;
                }

                return (tsqlType, comment);
            }

            // 未找到映射,返回原类型并提示
            return (mysqlType, $" -- ⚠️ 未找到 MySQL 类型 '{baseType}' 的映射,暂未转换,请手动确认");
        }
    }
}

🧩 FunctionMapper(MySQL 函数 → T-SQL 函数映射)

功能:

  • 输入:MySQL 函数,如 IFNULL(x,y)CONCAT(x,y)NOW()DATEDIFF(...)
  • 输出:对应的 T-SQL 函数,如 ISNULL(x,y)x + yGETDATE(),并附带注释说明

✅ 代码实现:FunctionMapper.cs

csharp 复制代码
using System;
using System.Collections.Generic;

namespace MySqlToTSqlMigrator
{
    public static class FunctionMapper
    {
        private static readonly Dictionary<string, string> FunctionMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
        {
            // 逻辑/空值处理
            { "IFNULL", "ISNULL" },           // IFNULL(x,y) → ISNULL(x,y)
            { "NULLIF", "NULLIF" },           // NULLIF 相同

            // 字符串连接
            { "CONCAT", "CONCAT" },           // T-SQL 也支持 CONCAT,但也可以手动用 +
            // 如果想强制用 +,可以额外处理

            // 日期时间
            { "NOW", "GETDATE" },             // NOW() → GETDATE()
            { "CURRENT_TIMESTAMP", "GETDATE" },
            { "CURDATE", "CAST(GETDATE() AS DATE)" },
            { "CURTIME", "CONVERT(TIME, GETDATE())" },

            // 数学 / 其它
            { "IF", "IIF" },                  // MySQL IF(cond, a, b) → T-SQL IIF(cond, a, b) 或 CASE
            // 更复杂的 IF 可能需要用 CASE 替代,此处简化为 IIF

            // 字符串处理(可扩展)
            { "UCASE", "UPPER" },
            { "LCASE", "LOWER" },
            { "LENGTH", "LEN" },
            { "CHAR_LENGTH", "LEN" }
        };

        /// <summary>
        /// 将 MySQL 函数名映射为 T-SQL 函数,返回转换后的函数名和注释
        /// </summary>
        /// <param name="mysqlFunc">如 "IFNULL", "NOW"</param>
        /// <returns>(TSqlFunc, Comment)</returns>
        public static (string TSqlFunction, string Comment) Map(string mysqlFunc)
        {
            if (string.IsNullOrWhiteSpace(mysqlFunc))
                return (mysqlFunc, "⚠️ 未识别的函数");

            if (FunctionMap.TryGetValue(mysqlFunc.ToUpperInvariant(), out string tsqlFunc))
            {
                string comment = "";
                if (mysqlFunc.Equals("IFNULL", StringComparison.OrdinalIgnoreCase))
                    comment = " -- [映射说明] MySQL IFNULL(x,y) → T-SQL ISNULL(x,y)";
                else if (mysqlFunc.Equals("NOW", StringComparison.OrdinalIgnoreCase))
                    comment = " -- [映射说明] MySQL NOW() → T-SQL GETDATE()";
                else if (mysqlFunc.Equals("IF", StringComparison.OrdinalIgnoreCase))
                    comment = " -- [映射说明] MySQL IF(cond,a,b) → T-SQL IIF(cond,a,b)(或用 CASE WHEN)";

                return (tsqlFunc, comment);
            }

            return (mysqlFunc, $" -- ⚠️ 未找到 MySQL 函数 '{mysqlFunc}' 的映射,暂未转换");
        }
    }
}

🧩 ControlFlowConverter(控制流语句转换,如 REPEAT → WHILE)

功能:

  • 输入:MySQL 控制流关键字,如 REPEAT ... UNTILLEAVEITERATE
  • 输出:对应的 T-SQL 控制流结构,如 WHILE NOT (条件)BREAK,并附带注释说明

✅ 代码实现:ControlFlowConverter.cs

csharp 复制代码
namespace MySqlToTSqlMigrator
{
    public static class ControlFlowConverter
    {
        /// <summary>
        /// 将 MySQL 控制流关键字/结构转换为 T-SQL,并返回转换后的语句和注释
        /// </summary>
        /// <param name="mysqlKeyword">如 "REPEAT", "LEAVE", "ITERATE"</param>
        /// <returns>(TSqlCode, Comment)</returns>
        public static (string TSqlCode, string Comment) Convert(string mysqlKeyword)
        {
            switch (mysqlKeyword.ToUpperInvariant())
            {
                case "REPEAT":
                    return (
                        "WHILE 1=1 BEGIN -- [转换说明] MySQL REPEAT → T-SQL WHILE 1=1(需配合 UNTIL 逻辑改为 IF + BREAK)",
                        " -- [转换说明] REPEAT ... UNTIL 转为 WHILE + IF + BREAK");

                case "UNTIL":
                    return (
                        "IF (条件) BREAK; END -- [转换说明] MySQL UNTIL 条件 → T-SQL IF (条件) BREAK",
                        " -- [转换说明] UNTIL 条件判断后退出循环");

                case "LEAVE":
                    return ("BREAK", " -- [转换说明] MySQL LEAVE → T-SQL BREAK");

                case "ITERATE":
                    return ("CONTINUE", " -- [转换说明] MySQL ITERATE → T-SQL CONTINUE");

                default:
                    return ($"{mysqlKeyword}", $" -- ⚠️ 未处理的控制流关键字 '{mysqlKeyword}',请手动转换");
            }
        }
    }
}

🧠 提示:对于 REPEAT ... UNTIL,推荐在解析阶段识别整个块,然后转换为:

sql 复制代码
WHILE 1=1
BEGIN
   -- 循环体
   IF (条件) BREAK;
END

可在 MySqlParserTSqlGenerator 中进一步实现完整块级转换。


🧩 Models/CodeBlock.cs(代码块模型)

csharp 复制代码
namespace MySqlToTSqlMigrator.Models
{
    /// <summary>
    /// 表示解析后的一种代码块类型
    /// </summary>
    public enum BlockType
    {
        ProcedureHeader,  // CREATE PROCEDURE
        VariableDeclaration, // DECLARE
        ControlFlow,      // IF / WHILE / REPEAT 等
        DmlStatement,     // INSERT / UPDATE / DELETE
        QuerySelect,      // SELECT 查询
        CursorOperation,  // DECLARE CURSOR / FETCH
        Transaction,      // BEGIN / COMMIT / ROLLBACK
        DynamicSql,       // PREPARE / EXECUTE
        TempTable,        // CREATE TEMPORARY TABLE
        ExceptionHandler, // DECLARE HANDLER
        Other             // 其它 / 未分类
    }

    public class CodeBlock
    {
        public BlockType Type { get; set; }
        public string OriginalText { get; set; }
        public string ConvertedText { get; set; }
        public string Comment { get; set; } // 转换说明注释

        public CodeBlock(BlockType type, string original, string converted = "", string comment = "")
        {
            Type = type;
            OriginalText = original;
            ConvertedText = converted;
            Comment = comment;
        }
    }
}

🧩 Models/VariableDeclaration.cs(变量声明模型 - 可扩展)

csharp 复制代码
namespace MySqlToTSqlMigrator.Models
{
    public class VariableDeclaration
    {
        public string Name { get; set; }
        public string MySqlType { get; set; }
        public string TSqlType { get; set; }
        public bool IsOut { get; set; } = false;

        public VariableDeclaration(string name, string mySqlType, bool isOut = false)
        {
            Name = name;
            MySqlType = mySqlType;
            IsOut = isOut;
        }
    }
}

✅ 得到:

功能 是否实现 说明
✅ 解析函数调用(IFNULL, NOW, CONCAT)并替换 带注释说明
✅ 解析变量类型(INT, VARCHAR, TEXT)并映射 使用 TypeMapper
✅ 移除 DML 中的 LIMIT 并加注释 正则匹配并清除
✅ 解析变量赋值(SET / SELECT) 暂保留,可扩展
✅ 解析控制流(REPEAT → WHILE 等) 调用 ControlFlowConverter
✅ 解析游标、异常、事务,生成注释 提示手动优化
✅ 生成完整带注释的 T-SQL 每个块都有转换说明
相关推荐
"菠萝"4 小时前
C#知识学习-018(方法参数传递)
学习·c#·1024程序员节
CiLerLinux5 小时前
第三章 FreeRTOS 任务相关 API 函数
开发语言·单片机·物联网·c#
.NET修仙日记5 小时前
C#/.NET 微服务架构:从入门到精通的完整学习路线
微服务·c#·.net·.net core·分布式架构·技术进阶
守城小轩14 小时前
基于Chrome140的FB账号自动化(关键词浏览)——运行脚本(三)
自动化·rpa·浏览器自动化
大飞记Python15 小时前
实战分享:一键自动化下载指定版本的Chrome及Chromedriver(附Python源码)
chrome·python·自动化
歪歪10015 小时前
在C#中详细介绍一下Visual Studio中如何使用数据可视化工具
开发语言·前端·c#·visual studio code·visual studio·1024程序员节
Eiceblue15 小时前
如何通过 C# 高效读写 Excel 工作表
c#·visual studio·1024程序员节
张人玉15 小时前
WPF 触发器详解:定义、种类与示例
c#·wpf·1024程序员节·布局控件
TG_yunshuguoji18 小时前
亚马逊云渠道商:AWS实例自动替换策略在哪里设置?
运维·服务器·云计算·aws