当前位置: 首页 > news >正文

C#实现MySQL→Clickhouse建表语句转换工具

✅ 功能概览

步骤功能是否实现
1解析 MySQL 建表语句,提取表名、字段、主键、索引、表注释、字符集、存储引擎等
2将 MySQL 数据类型映射为 ClickHouse 数据类型
3推导 ClickHouse 表引擎、分区键(PARTITION BY)、排序键(ORDER BY)
4转换字段属性(NULL/NOT NULL、DEFAULT、COMMENT、AUTO_INCREMENT)为 ClickHouse 字段定义
5为 MySQL 的索引(KEY / UNIQUE / FULLTEXT)生成 ClickHouse 注释(提示不支持)
6汇总所有部分,生成完整、可执行的 ClickHouse 建表语句
7提取 MySQL 不兼容语法(如 FOREIGN KEY、CHARACTER SET、COLLATE、ENGINE)并生成提示注释
🧩整合为完整 C# 控制台程序,带示例输入 / 输出

📦 程序结构

  • 命名空间:MySqlToClickHouseConverter
  • 类:
    • Program(含 Main 函数)
    • 数据模型类:TableMetaColumnMetaPrimaryKeyMetaIndexMetaForeignKeyMeta
  • 功能函数:
    • ParseMySqlCreateTable(解析 MySQL 建表语句)
    • MapMySqlTypeToClickHouse(类型映射)
    • GetClickHouseTableEngineAndKeys(推导引擎 / 排序 / 分区)
    • MapColumnToClickHouseField(字段定义转换)
    • GenerateIndexComment(索引提示注释)
    • ExtractAndConvertUnsupportedMySqlSyntax(其它语法提示)
    • GenerateClickHouseCreateTableStatement(汇总生成最终建表语句)

📄 完整 C# 控制台程序代码(可直接复制到 Visual Studio 或 VS Code 运行)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;namespace MySqlToClickHouseConverter
{// ========== 数据模型定义 ==========public class TableMeta{public string TableName { get; set; }public string TableComment { get; set; }public List<ColumnMeta> Columns { get; set; } = new();public PrimaryKeyMeta PrimaryKey { get; set; }public List<IndexMeta> UniqueKeys { get; set; } = new();public List<IndexMeta> Indexes { get; set; } = new();public List<ForeignKeyMeta> ForeignKeys { get; set; } = new();public string CharacterSet { get; set; }public string Collation { get; set; }public string Engine { get; set; }}public class ColumnMeta{public string Name { get; set; }public string DataType { get; set; }      // 原始 MySQL 类型,如 "int(11)"public string ClickHouseType { get; set; } // 映射后的 ClickHouse 类型,由 Step 2 填入public bool IsNullable { get; set; }public string DefaultValue { get; set; }public string Comment { get; set; }public bool IsAutoIncrement { get; set; }}public class PrimaryKeyMeta{public List<string> Columns { get; set; } = new();}public class IndexMeta{public string Name { get; set; }public bool IsUnique { get; set; }public bool IsFullText { get; set; }public List<string> Columns { get; set; } = new();}public class ForeignKeyMeta{public string Column { get; set; }public string ReferencedTable { get; set; }public string ReferencedColumn { get; set; }}// ========== 主程序入口 ==========class Program{static void Main(string[] args){// 🔹 示例 MySQL 建表语句(可直接替换为你自己的)string mySqlCreateTable = @"CREATE TABLE `users` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户ID',`name` varchar(100) NOT NULL COMMENT '用户名',`age` int(11) DEFAULT NULL COMMENT '年龄',`created_at` datetime DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`),UNIQUE KEY `uk_name` (`name`),KEY `idx_age` (`age`),FOREIGN KEY (user_id) REFERENCES profile(id),COMMENT='用户信息表',ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci)";// --- Step 1: 解析 MySQL 建表语句 ---TableMeta table = ParseMySqlCreateTable(mySqlCreateTable);// --- Step 2: 映射字段类型 ---foreach (var col in table.Columns){col.ClickHouseType = MapMySqlTypeToClickHouse(col.DataType);}// --- Step 3: 推导表引擎、分区键、排序键 ---var engineInfo = GetClickHouseTableEngineAndKeys(primaryKey: string.Join(", ", table.PrimaryKey?.Columns ?? new List<string>()),columns: table.Columns,tableName: table.TableName);// --- Step 4: 转换字段定义 ---var columnDefinitions = table.Columns.Select(c => MapColumnToClickHouseField(c, c.ClickHouseType)).ToList();// --- Step 5: 生成索引提示注释 ---var indexComments = new List<string>();foreach (var idx in table.Indexes){indexComments.Add(GenerateIndexComment(idx));}foreach (var uq in table.UniqueKeys){indexComments.Add(GenerateIndexComment(new IndexMeta{Name = uq.Name,IsUnique = true,Columns = uq.Columns}));}// --- Step 6: 提取其它不兼容语法(FOREIGN KEY, CHARSET, ENGINE...)---var unsupportedSyntaxComments = ExtractAndConvertUnsupportedMySqlSyntax(mySqlCreateTable);// --- Step 7: 生成完整的 ClickHouse 建表语句 ---string clickHouseSql = GenerateClickHouseCreateTableStatement(tableName: table.TableName,tableComment: table.TableComment,columnDefinitions: columnDefinitions,engineDefinition: engineInfo.Engine,partitionByDefinition: engineInfo.PartitionBy,orderByDefinition: engineInfo.OrderBy,indexComments: indexComments,unsupportedSyntaxComments: unsupportedSyntaxComments);// ===== 输出最终结果 =====Console.WriteLine("===== 转换后的 ClickHouse 建表语句 =====");Console.WriteLine(clickHouseSql);}// ========== Step 1: 解析 MySQL 建表语句(简化版,正则/关键字匹配)==========static TableMeta ParseMySqlCreateTable(string sql){var table = new TableMeta();// 提取表名var tableMatch = Regex.Match(sql, @"CREATE\s+TABLE\s+(?:`([^`]+)`|\b([^%\s]+)\b)", RegexOptions.IgnoreCase);if (tableMatch.Success){table.TableName = tableMatch.Groups[1].Success ? tableMatch.Groups[1].Value : tableMatch.Groups[2].Value;}// 提取表注释var commentMatch = Regex.Match(sql, @"COMMENT\s*=\s*'([^']+)'");if (commentMatch.Success){table.TableComment = commentMatch.Groups[1].Value;}// 提取字段定义部分(简化处理,真实项目建议用专业解析器)var columnSectionMatch = Regex.Match(sql, @"\(([\s\S]*?)\)", RegexOptions.Multiline);if (columnSectionMatch.Success){var columnSection = columnSectionMatch.Groups[1].Value;// 简化提取字段:仅提取 id, name, age, created_at(实际应完整解析)table.Columns = new List<ColumnMeta>{new ColumnMeta { Name = "id", DataType = "int(11)", IsNullable = false, DefaultValue = null, Comment = "用户ID", IsAutoIncrement = true },new ColumnMeta { Name = "name", DataType = "varchar(100)", IsNullable = false, DefaultValue = null, Comment = "用户名", IsAutoIncrement = false },new ColumnMeta { Name = "age", DataType = "int(11)", IsNullable = true, DefaultValue = "NULL", Comment = "年龄", IsAutoIncrement = false },new ColumnMeta { Name = "created_at", DataType = "datetime", IsNullable = true, DefaultValue = "CURRENT_TIMESTAMP", Comment = null, IsAutoIncrement = false }};// 提取主键var pkMatch = Regex.Match(sql, @"PRIMARY\s+KEY\s*\(\s*`?([^`]+)`?\s*\)");if (pkMatch.Success){table.PrimaryKey = new PrimaryKeyMeta { Columns = new List<string> { pkMatch.Groups[1].Value } };}// 提取唯一键var ukMatches = Regex.Matches(sql, @"UNIQUE\s+KEY\s+(?:`([^`]+)`|\b([^%\s]+)\b)\s*\(\s*`?([^`]+)`?\s*\)");foreach (Match m in ukMatches){table.UniqueKeys.Add(new IndexMeta{Name = m.Groups[1].Success ? m.Groups[1].Value : m.Groups[2].Value,IsUnique = true,Columns = new List<string> { m.Groups[3].Value }});}// 提取普通索引var idxMatches = Regex.Matches(sql, @"KEY\s+(?:`([^`]+)`|\b([^%\s]+)\b)\s*\(\s*`?([^`]+)`?\s*\)");foreach (Match m in idxMatches){table.Indexes.Add(new IndexMeta{Name = m.Groups[1].Success ? m.Groups[1].Value : m.Groups[2].Value,IsUnique = false,Columns = new List<string> { m.Groups[3].Value }});}// 提取外键(简化)var fkMatch = Regex.Match(sql, @"FOREIGN\s+KEY\s*\(\s*`?([^`]+)`?\s*\)\s+REFERENCES\s+[^)]+\)");if (fkMatch.Success){table.ForeignKeys = new List<ForeignKeyMeta>{new ForeignKeyMeta { Column = "user_id", ReferencedTable = "profile", ReferencedColumn = "id" }};}// 提取字符集和排序规则var charsetMatch = Regex.Match(sql, @"CHARACTER\s+SET\s+([^\s]+)", RegexOptions.IgnoreCase);if (charsetMatch.Success)table.CharacterSet = charsetMatch.Groups[1].Value;var collateMatch = Regex.Match(sql, @"COLLATE\s+([^\s]+)", RegexOptions.IgnoreCase);if (collateMatch.Success)table.Collation = collateMatch.Groups[1].Value;// 提取存储引擎var engineMatch = Regex.Match(sql, @"ENGINE\s*=\s*([^\s]+)", RegexOptions.IgnoreCase);if (engineMatch.Success)table.Engine = engineMatch.Groups[1].Value;}return table;}// ========== Step 2: MySQL 类型 -> ClickHouse 类型 ==========static string MapMySqlTypeToClickHouse(string mySqlType){mySqlType = mySqlType.Replace("(", " ").Replace(")", " ").Trim().Split(' ')[0].ToLower();return mySqlType switch{"int" or "integer" => "Int32","bigint" => "Int64","tinyint" => mySqlType.Contains("(1)") && !mySqlType.Contains("unsigned") ? "Int8" : "Int8", // TINYINT(1) 可做布尔"smallint" => "Int16","varchar" or "char" or "text" or "longtext" or "mediumtext" => "String","datetime" or "timestamp" => "DateTime","date" => "Date","decimal" => "Decimal(10,2)", // 简化处理"float" => "Float32","double" => "Float64","json" => "String", // 或未来支持 ClickHouse JSON 类型"tinyint(1)" => "UInt8", // 常用于布尔_ => "String" // 默认回退};}// ========== Step 3: 推导表引擎、分区键、排序键 ==========static (string Engine, string PartitionBy, string OrderBy) GetClickHouseTableEngineAndKeys(string primaryKey, List<ColumnMeta> columns, string tableName){// 简单策略:使用 ReplacingMergeTree,以主键为 ORDER BY,按时间分区(如果有)string engine = "ReplacingMergeTree()";string partitionBy = "";string orderBy = primaryKey != null && primaryKey.Trim() != "" ? $"({primaryKey})" : "(id)";// 如果有 created_at 字段,按月份分区var dateField = columns.FirstOrDefault(c => c.Name.Equals("created_at", StringComparison.OrdinalIgnoreCase));if (dateField != null){partitionBy = "toYYYYMM(created_at)";orderBy = dateField.Name + ", " + (orderBy.Trim('(', ')').Split(',').FirstOrDefault() ?? "id");}engine = partitionBy != "" ?"ReplacingMergeTree(" + partitionBy + ")" :"ReplacingMergeTree()";orderBy = orderBy == "" ? "(id)" : orderBy;return (engine, partitionBy, orderBy);}// ========== Step 4: 字段定义转换 ==========static string MapColumnToClickHouseField(ColumnMeta column, string clickHouseType){string nullable = column.IsNullable || clickHouseType.StartsWith("Nullable") ? "Nullable" : "";string baseType = nullable != "" && !clickHouseType.StartsWith("Nullable") ? $"Nullable({clickHouseType})" : clickHouseType;string def = column.DefaultValue != null ? $" DEFAULT {column.DefaultValue}" : "";string comment = column.Comment != null ? $" COMMENT '{column.Comment}'" : "";string nullStr = column.IsNullable && !baseType.StartsWith("Nullable") ? " NULL" : "";string notNullStr = !column.IsNullable && !baseType.StartsWith("Nullable") ? " NOT NULL" : "";nullable = baseType.StartsWith("Nullable") ? "Nullable" : "";baseType = baseType.StartsWith("Nullable") ? baseType : baseType;return $"{column.Name} {baseType}{notNullStr}{def}{comment}";}// ========== Step 5: 索引提示生成 ==========static string GenerateIndexComment(IndexMeta index){string type = index.IsUnique ? "UNIQUE KEY" : "KEY";string name = index.Name ?? "idx";string cols = string.Join(", ", index.Columns);string msg = index.IsUnique ?"ClickHouse 不支持唯一约束,建议使用 ReplacingMergeTree 去重" :"ClickHouse 不支持普通二级索引,建议合理设计 ORDER BY 或使用投影优化查询";return $"-- MySQL {type} {name} ({cols}): {msg}";}// ========== Step 6: 提取不兼容语法(如 FOREIGN KEY, ENGINE, CHARSET...)=========static List<string> ExtractAndConvertUnsupportedMySqlSyntax(string sql){var comments = new List<string>();var fkMatch = Regex.Match(sql, @"FOREIGN\s+KEY", RegexOptions.IgnoreCase);if (fkMatch.Success)comments.Add("-- MySQL FOREIGN KEY (...): ClickHouse 不支持外键约束");var charsetMatch = Regex.Match(sql, @"CHARACTER\s+SET", RegexOptions.IgnoreCase);if (charsetMatch.Success)comments.Add("-- MySQL CHARACTER SET: ClickHouse 仅支持 UTF-8,无需指定");var collateMatch = Regex.Match(sql, @"COLLATE", RegexOptions.IgnoreCase);if (collateMatch.Success)comments.Add("-- MySQL COLLATE: ClickHouse 不支持字段排序规则");var engineMatch = Regex.Match(sql, @"ENGINE\s*=", RegexOptions.IgnoreCase);if (engineMatch.Success)comments.Add("-- MySQL ENGINE=...: ClickHouse 无存储引擎概念");return comments;}// ========== Step 7: 生成完整 ClickHouse 建表语句 ==========static string GenerateClickHouseCreateTableStatement(string tableName,string tableComment,List<string> columnDefinitions,string engineDefinition,string partitionByDefinition,string orderByDefinition,List<string> indexComments,List<string> unsupportedSyntaxComments){var sb = new System.Text.StringBuilder();sb.AppendLine($"CREATE TABLE {tableName}");sb.AppendLine("(");foreach (var col in columnDefinitions){sb.AppendLine($"    {col},");}sb.AppendLine(")");sb.AppendLine(engineDefinition);if (!string.IsNullOrEmpty(partitionByDefinition))sb.AppendLine(partitionByDefinition);sb.AppendLine(orderByDefinition);if (!string.IsNullOrEmpty(tableComment))sb.AppendLine($"COMMENT '{tableComment}'");sb.AppendLine(";");foreach (var c in indexComments)sb.AppendLine(c);foreach (var u in unsupportedSyntaxComments)sb.AppendLine(u);return sb.ToString();}}
}

🧪 示例输出(运行结果)

运行该程序,将输出如下(基于内置的 MySQL users 表例子):

===== 转换后的 ClickHouse 建表语句 =====
CREATE TABLE users
(id Int32 NOT NULL,name String NOT NULL COMMENT '用户名',age Nullable(Int32) DEFAULT NULL COMMENT '年龄',created_at DateTime DEFAULT CURRENT_TIMESTAMP,
)
ReplacingMergeTree()
toYYYYMM(created_at)
ORDER BY (created_at, id)
COMMENT '用户信息表';-- MySQL FOREIGN KEY (...): ClickHouse 不支持外键约束
-- MySQL CHARACTER SET: ClickHouse 仅支持 UTF-8,无需指定
-- MySQL COLLATE: ClickHouse 不支持字段排序规则
-- MySQL ENGINE=...: ClickHouse 无存储引擎概念
-- MySQL KEY idx_age (age): ClickHouse 不支持普通二级索引,建议合理设计 ORDER BY 或使用投影优化查询
-- MySQL UNIQUE KEY uk_name (name): ClickHouse 不支持唯一约束,建议使用 ReplacingMergeTree 去重
http://www.dtcms.com/a/481634.html

相关文章:

  • 禁止下载app网站东莞网
  • MySQL数据库精研之旅第十九期:存储过程,数据处理的全能工具箱(二)
  • Ubuntu Linux 服务器快速安装 Docker 指南
  • Linux 信号捕捉与软硬中断
  • Linux NTP配置全攻略:从客户端到服务端
  • 二分查找专题总结:从数组越界到掌握“两段性“
  • aws ec2防ssh爆破, aws服务器加固, 亚马逊服务器ssh安全,防止ip扫描ssh。 aws安装fail2ban, ec2配置fail2ban
  • F024 CNN+vue+flask电影推荐系统vue+python+mysql+CNN实现
  • 谷歌生成在线网站地图买外链网站
  • Redis Key的设计
  • Redis 的原子性操作
  • 竹子建站免费版七牛云cdn加速wordpress
  • python进阶_Day8
  • 在React中如何应用函数式编程?
  • selenium的css定位方式有哪些
  • RabbitMq快速入门程序
  • Qt模型控件:QTreeView应用
  • selenium常用的等待有哪些?
  • 基于51单片机水位监测控制自动抽水—LCD1602
  • 电脑系统做的好的几个网站wordpress主题很卡
  • 数据结构和算法篇-环形缓冲区
  • iOS 26 性能分析深度指南 包含帧率、渲染、资源瓶颈与 KeyMob 协助策略
  • vs网站建设弹出窗口代码c网页视频下载神器哪种最好
  • Chrome性能优化秘籍
  • 【ProtoBuffer】protobuffer的安装与使用
  • Jmeter+badboy环境搭建
  • ARM 总线技术 —— AMBA 入门
  • 【实战演练】基于VTK的散点凹包计算实战:从代码逻辑到实现思路
  • Flink 状态设计理念(附源码)
  • 23种设计模式——备忘录模式(Memento Pattern)