Program.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.Data.SqlClient;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using MySql.Data.MySqlClient;
  9. using Newtonsoft.Json;
  10. namespace DataMigration
  11. {
  12. class Program
  13. {
  14. static void Main(string[] args)
  15. {
  16. Console.WriteLine("开始数据迁移...");
  17. try
  18. {
  19. // 读取配置
  20. var config = ReadConfig();
  21. // 迁移TugboatCommon数据库
  22. Console.WriteLine("\n迁移TugboatCommon数据库...");
  23. MigrateTugboatCommon(config);
  24. // 迁移LiandaTugboatMIS数据库
  25. Console.WriteLine("\n迁移LiandaTugboatMIS数据库...");
  26. MigrateLiandaTugboatMIS(config);
  27. Console.WriteLine("\n数据迁移完成!");
  28. }
  29. catch (Exception ex)
  30. {
  31. Console.WriteLine($"迁移过程中出错: {ex.Message}");
  32. Console.WriteLine(ex.StackTrace);
  33. }
  34. Console.WriteLine("\n按任意键退出...");
  35. Console.ReadKey();
  36. }
  37. static Config ReadConfig()
  38. {
  39. var configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json");
  40. var json = File.ReadAllText(configPath);
  41. return JsonConvert.DeserializeObject<Config>(json);
  42. }
  43. static void MigrateTugboatCommon(Config config)
  44. {
  45. using (var sqlConn = new SqlConnection(config.ConnectionStrings.SqlServerCommonDataContext))
  46. using (var mysqlConn = new MySqlConnection(config.ConnectionStrings.MySqlTugboatCommon))
  47. {
  48. sqlConn.Open();
  49. mysqlConn.Open();
  50. Console.WriteLine("连接数据库成功");
  51. // 动态获取SQL Server中的所有表名
  52. var tablesToMigrate = GetAllSqlServerTables(sqlConn);
  53. Console.WriteLine($"在SQL Server中找到{tablesToMigrate.Count}个表:");
  54. foreach (var tableName in tablesToMigrate)
  55. {
  56. Console.WriteLine($"- {tableName}");
  57. }
  58. // 首先删除所有表,以避免外键约束的干扰
  59. DropAllTables(mysqlConn, tablesToMigrate);
  60. // 迁移所有表
  61. foreach (var tableName in tablesToMigrate)
  62. {
  63. MigrateTable(sqlConn, mysqlConn, tableName);
  64. }
  65. Console.WriteLine("TugboatCommon数据库迁移完成");
  66. }
  67. }
  68. static void DropAllTables(MySqlConnection conn, List<string> tables)
  69. {
  70. Console.WriteLine("\n开始删除所有表...");
  71. // 禁用外键约束检查
  72. using (var cmd = new MySqlCommand("SET FOREIGN_KEY_CHECKS = 0", conn))
  73. {
  74. cmd.ExecuteNonQuery();
  75. }
  76. // 禁用唯一约束检查
  77. using (var cmd = new MySqlCommand("SET UNIQUE_CHECKS = 0", conn))
  78. {
  79. cmd.ExecuteNonQuery();
  80. }
  81. // 逆序删除表,以避免外键约束问题
  82. for (int i = tables.Count - 1; i >= 0; i--)
  83. {
  84. string tableName = tables[i];
  85. try
  86. {
  87. using (var cmd = new MySqlCommand($"DROP TABLE IF EXISTS {tableName}", conn))
  88. {
  89. cmd.ExecuteNonQuery();
  90. Console.WriteLine($"删除表: {tableName}");
  91. }
  92. }
  93. catch (Exception ex)
  94. {
  95. Console.WriteLine($"删除表{tableName}时出错: {ex.Message}");
  96. }
  97. }
  98. Console.WriteLine("删除所有表完成");
  99. }
  100. static void MigrateLiandaTugboatMIS(Config config)
  101. {
  102. using (var sqlConn = new SqlConnection(config.ConnectionStrings.SqlServerDataContext))
  103. using (var mysqlConn = new MySqlConnection(config.ConnectionStrings.MySqlLiandaTugboatMIS))
  104. {
  105. sqlConn.Open();
  106. mysqlConn.Open();
  107. Console.WriteLine("连接数据库成功");
  108. // 动态获取SQL Server中的所有表名
  109. var allTables = GetAllSqlServerTables(sqlConn);
  110. Console.WriteLine($"在SQL Server中找到{allTables.Count}个表:");
  111. foreach (var tableName in allTables)
  112. {
  113. Console.WriteLine($"- {tableName}");
  114. }
  115. // 首先删除所有表,以避免外键约束的干扰
  116. DropAllTables(mysqlConn, allTables);
  117. // 定义无外键依赖的表名模式
  118. var independentTablePatterns = new List<string>
  119. {
  120. "Sys_User",
  121. "Sys_Role",
  122. "Sys_Menu",
  123. "Sys_Function",
  124. "Sys_Dictionary",
  125. "Fin_AssistCode",
  126. "Fin_Subject",
  127. "Fin_TaxRule",
  128. "Pro_FeeItemSettings",
  129. "Xh_ReceiptDictionary",
  130. "Bus_Holiday",
  131. "Bus_ShipPaymentType",
  132. "Disp_Port",
  133. "Disp_PortDictionary",
  134. "Sal_Department",
  135. "Sal_Employee"
  136. };
  137. // 先迁移无外键依赖的表
  138. var independentTables = allTables.Where(t => independentTablePatterns.Any(p => t.Contains(p))).ToList();
  139. Console.WriteLine($"\n先迁移无外键依赖的{independentTables.Count}个表:");
  140. foreach (var tableName in independentTables)
  141. {
  142. MigrateTable(sqlConn, mysqlConn, tableName);
  143. }
  144. // 再迁移其他表(可能有外键依赖)
  145. var dependentTables = allTables.Except(independentTables).ToList();
  146. Console.WriteLine($"\n再迁移其他{dependentTables.Count}个表:");
  147. foreach (var tableName in dependentTables)
  148. {
  149. MigrateTable(sqlConn, mysqlConn, tableName);
  150. }
  151. Console.WriteLine("LiandaTugboatMIS数据库迁移完成");
  152. }
  153. }
  154. static void MigrateTable(SqlConnection sqlConn, MySqlConnection mysqlConn, string tableName)
  155. {
  156. // 检查是否是Sys_Log表(操作日志,数据量很大,对系统逻辑没有太大帮助)
  157. bool isSysLogTable = tableName.Equals("Sys_Log", StringComparison.OrdinalIgnoreCase) ||
  158. tableName.Equals("Sys_log", StringComparison.OrdinalIgnoreCase);
  159. Console.WriteLine($"\n迁移{tableName}表...");
  160. try
  161. {
  162. // 从SQL Server读取表结构
  163. DataTable schemaTable = GetSqlServerTableSchema(sqlConn, tableName);
  164. Console.WriteLine($"读取SQL Server表结构完成,包含{schemaTable.Columns.Count}列");
  165. // 打印SQL Server表的所有列信息
  166. Console.WriteLine("SQL Server表列信息:");
  167. foreach (DataColumn column in schemaTable.Columns)
  168. {
  169. Console.WriteLine($" - {column.ColumnName} ({column.DataType.Name}, AllowDBNull: {column.AllowDBNull})");
  170. }
  171. // 确保MySQL表结构与SQL Server一致
  172. EnsureTableStructure(mysqlConn, sqlConn, tableName, schemaTable);
  173. // 如果是Sys_Log表,只迁移表结构,不迁移数据
  174. if (isSysLogTable)
  175. {
  176. Console.WriteLine($"跳过{tableName}表的数据迁移(操作日志,数据量较大)");
  177. return;
  178. }
  179. // 读取SQL Server数据
  180. var dataTable = ReadTableFromSqlServer(sqlConn, tableName);
  181. Console.WriteLine($"从SQL Server读取到{dataTable.Rows.Count}条记录");
  182. if (dataTable.Rows.Count > 0)
  183. {
  184. // 清空MySQL表
  185. ClearMySqlTable(mysqlConn, tableName);
  186. // 写入MySQL
  187. WriteTableToMySql(mysqlConn, tableName, dataTable);
  188. Console.WriteLine($"成功写入MySQL");
  189. }
  190. }
  191. catch (Exception ex)
  192. {
  193. if (ex.Message.Contains("对象名 '" + tableName + "' 无效"))
  194. {
  195. Console.WriteLine($"SQL Server中不存在表{tableName},跳过迁移");
  196. }
  197. else
  198. {
  199. Console.WriteLine($"迁移过程中出错: {ex.Message}");
  200. throw;
  201. }
  202. }
  203. }
  204. static DataTable GetSqlServerTableSchema(SqlConnection sqlConn, string tableName)
  205. {
  206. string query = $"SELECT * FROM {tableName} WHERE 1=0";
  207. using (SqlCommand cmd = new SqlCommand(query, sqlConn))
  208. using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
  209. {
  210. DataTable schemaTable = new DataTable();
  211. adapter.FillSchema(schemaTable, SchemaType.Source);
  212. return schemaTable;
  213. }
  214. }
  215. static string EscapeMySqlIdentifier(string identifier)
  216. {
  217. // MySQL保留关键字列表
  218. var reservedKeywords = new HashSet<string>
  219. {
  220. "MAXVALUE", "MINVALUE", "ORDER", "GROUP", "BY", "SELECT", "INSERT", "UPDATE", "DELETE", "FROM", "WHERE", "JOIN", "ON", "IN", "NOT", "AND", "OR", "LIKE", "LIMIT"
  221. };
  222. // 如果是保留关键字,使用反引号包围
  223. if (reservedKeywords.Contains(identifier.ToUpper()))
  224. {
  225. return $"`{identifier}`";
  226. }
  227. return identifier;
  228. }
  229. static void EnsureTableStructure(MySqlConnection conn, SqlConnection sqlConn, string tableName, DataTable schemaTable)
  230. {
  231. Console.WriteLine($"检查{tableName}表结构...");
  232. // 执行一系列SQL语句来禁用所有可能的约束检查
  233. string[] disableConstraintsSql = new string[]
  234. {
  235. "SET FOREIGN_KEY_CHECKS = 0",
  236. "SET UNIQUE_CHECKS = 0",
  237. "SET SQL_MODE = 'NO_ENGINE_SUBSTITUTION'",
  238. "SET AUTOCOMMIT = 0"
  239. };
  240. foreach (string sql in disableConstraintsSql)
  241. {
  242. try
  243. {
  244. using (var cmd = new MySqlCommand(sql, conn))
  245. {
  246. cmd.ExecuteNonQuery();
  247. Console.WriteLine($"执行SQL成功: {sql}");
  248. }
  249. }
  250. catch (Exception ex)
  251. {
  252. Console.WriteLine($"执行SQL时出错: {sql}, 错误信息: {ex.Message}");
  253. }
  254. }
  255. // 删除旧表
  256. try
  257. {
  258. using (var cmd = new MySqlCommand($"DROP TABLE IF EXISTS {tableName}", conn))
  259. {
  260. cmd.ExecuteNonQuery();
  261. Console.WriteLine($"删除旧表: {tableName}");
  262. }
  263. }
  264. catch (Exception ex)
  265. {
  266. Console.WriteLine($"删除表时出错: {ex.Message}");
  267. }
  268. // 从SQL Server获取主键信息
  269. var primaryKeys = GetSqlServerPrimaryKeys(sqlConn, tableName);
  270. Console.WriteLine($"从SQL Server获取到{primaryKeys.Count}个主键列: {string.Join(", ", primaryKeys)}");
  271. // 根据SQL Server表结构动态创建MySQL表
  272. var createTableSql = new StringBuilder();
  273. createTableSql.AppendLine($"CREATE TABLE IF NOT EXISTS {tableName} (");
  274. var columns = new List<string>();
  275. // 遍历SQL Server表的所有列
  276. foreach (DataColumn column in schemaTable.Columns)
  277. {
  278. string columnName = EscapeMySqlIdentifier(column.ColumnName);
  279. string mysqlType = GetMySqlDataType(column.DataType);
  280. // 构建列定义
  281. string columnDef = $" {columnName} {mysqlType}";
  282. // 检查是否允许空值
  283. if (!column.AllowDBNull)
  284. {
  285. columnDef += " NOT NULL";
  286. }
  287. columns.Add(columnDef);
  288. }
  289. // 添加主键约束
  290. if (primaryKeys.Count > 0)
  291. {
  292. var primaryKeyColumns = primaryKeys.Select(k => EscapeMySqlIdentifier(k)).ToList();
  293. columns.Add($" PRIMARY KEY ({string.Join(", ", primaryKeyColumns)})");
  294. Console.WriteLine($"为表{tableName}添加主键约束: {string.Join(", ", primaryKeys)}");
  295. }
  296. else
  297. {
  298. Console.WriteLine($"表{tableName}在SQL Server中没有主键");
  299. }
  300. // 添加列定义到创建表语句
  301. createTableSql.AppendLine(string.Join(",\n", columns));
  302. createTableSql.AppendLine(") ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;");
  303. // 执行创建表语句
  304. try
  305. {
  306. Console.WriteLine($"创建表SQL: {createTableSql.ToString().Substring(0, Math.Min(500, createTableSql.Length))}...");
  307. using (var cmd = new MySqlCommand(createTableSql.ToString(), conn))
  308. {
  309. cmd.ExecuteNonQuery();
  310. Console.WriteLine($"创建新表: {tableName}");
  311. Console.WriteLine($"表结构创建成功,包含{schemaTable.Columns.Count}列");
  312. if (primaryKeys.Count > 0)
  313. {
  314. Console.WriteLine($"已添加主键约束: {string.Join(", ", primaryKeys)}");
  315. }
  316. }
  317. }
  318. catch (Exception ex)
  319. {
  320. Console.WriteLine($"创建表时出错: {ex.Message}");
  321. // 如果创建表失败,尝试一个更简单的方法
  322. Console.WriteLine("尝试使用更简单的方法创建表...");
  323. string simpleCreateSql = $"CREATE TABLE IF NOT EXISTS {tableName} (";
  324. // 添加所有列,但使用最简单的定义
  325. var simpleColumns = new List<string>();
  326. foreach (DataColumn column in schemaTable.Columns)
  327. {
  328. string columnName = EscapeMySqlIdentifier(column.ColumnName);
  329. string mysqlType = "VARCHAR(255)";
  330. simpleColumns.Add($" {columnName} {mysqlType}");
  331. }
  332. // 添加主键约束
  333. if (primaryKeys.Count > 0)
  334. {
  335. var primaryKeyColumns = primaryKeys.Select(k => EscapeMySqlIdentifier(k)).ToList();
  336. simpleColumns.Add($" PRIMARY KEY ({string.Join(", ", primaryKeyColumns)})");
  337. }
  338. simpleCreateSql += string.Join(",\n", simpleColumns);
  339. simpleCreateSql += ") ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
  340. try
  341. {
  342. using (var cmd = new MySqlCommand(simpleCreateSql, conn))
  343. {
  344. cmd.ExecuteNonQuery();
  345. Console.WriteLine($"使用简单方法创建表成功: {tableName}");
  346. if (primaryKeys.Count > 0)
  347. {
  348. Console.WriteLine($"已添加主键约束: {string.Join(", ", primaryKeys)}");
  349. }
  350. }
  351. }
  352. catch (Exception simpleEx)
  353. {
  354. Console.WriteLine($"使用简单方法创建表时出错: {simpleEx.Message}");
  355. // 如果仍然失败,创建一个只有ID列的表
  356. try
  357. {
  358. string minimalCreateSql = $"CREATE TABLE IF NOT EXISTS {tableName} (ID VARCHAR(36) PRIMARY KEY) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci";
  359. using (var cmd = new MySqlCommand(minimalCreateSql, conn))
  360. {
  361. cmd.ExecuteNonQuery();
  362. Console.WriteLine($"使用最小方法创建表成功: {tableName}");
  363. Console.WriteLine("已添加默认主键约束: ID");
  364. }
  365. }
  366. catch (Exception minimalEx)
  367. {
  368. Console.WriteLine($"使用最小方法创建表时出错: {minimalEx.Message}");
  369. // 如果所有方法都失败,抛出异常
  370. throw new Exception($"无法创建表{tableName},请检查数据库配置和权限", minimalEx);
  371. }
  372. }
  373. }
  374. // 提交事务
  375. try
  376. {
  377. using (var cmd = new MySqlCommand("COMMIT", conn))
  378. {
  379. cmd.ExecuteNonQuery();
  380. Console.WriteLine("提交事务成功");
  381. }
  382. }
  383. catch (Exception ex)
  384. {
  385. Console.WriteLine($"提交事务时出错: {ex.Message}");
  386. }
  387. // 重新启用自动提交
  388. try
  389. {
  390. using (var cmd = new MySqlCommand("SET AUTOCOMMIT = 1", conn))
  391. {
  392. cmd.ExecuteNonQuery();
  393. Console.WriteLine("重新启用自动提交成功");
  394. }
  395. }
  396. catch (Exception ex)
  397. {
  398. Console.WriteLine($"重新启用自动提交时出错: {ex.Message}");
  399. }
  400. }
  401. static List<string> GetSqlServerPrimaryKeys(SqlConnection conn, string tableName)
  402. {
  403. var primaryKeys = new List<string>();
  404. try
  405. {
  406. // 使用sp_pkeys系统存储过程获取主键信息,这是获取表主键最可靠的方法
  407. using (var cmd = new SqlCommand("sp_pkeys", conn))
  408. {
  409. cmd.CommandType = CommandType.StoredProcedure;
  410. cmd.Parameters.AddWithValue("@table_name", tableName);
  411. cmd.Parameters.AddWithValue("@table_owner", DBNull.Value);
  412. cmd.Parameters.AddWithValue("@table_qualifier", DBNull.Value);
  413. Console.WriteLine($"执行存储过程获取主键: sp_pkeys @table_name = {tableName}");
  414. using (var reader = cmd.ExecuteReader())
  415. {
  416. while (reader.Read())
  417. {
  418. string columnName = reader["COLUMN_NAME"].ToString();
  419. primaryKeys.Add(columnName);
  420. Console.WriteLine($"找到主键列: {columnName}");
  421. }
  422. }
  423. }
  424. // 如果sp_pkeys没有返回结果,尝试使用sys系统视图
  425. if (primaryKeys.Count == 0)
  426. {
  427. Console.WriteLine("sp_pkeys未返回结果,尝试使用sys系统视图获取主键");
  428. string query = @"
  429. SELECT col.name AS COLUMN_NAME
  430. FROM sys.indexes AS idx
  431. INNER JOIN sys.index_columns AS ic ON idx.object_id = ic.object_id AND idx.index_id = ic.index_id
  432. INNER JOIN sys.columns AS col ON ic.object_id = col.object_id AND ic.column_id = col.column_id
  433. INNER JOIN sys.tables AS tab ON col.object_id = tab.object_id
  434. WHERE idx.is_primary_key = 1 AND tab.name = @TableName
  435. ORDER BY ic.key_ordinal
  436. ";
  437. using (var cmd = new SqlCommand(query, conn))
  438. {
  439. cmd.Parameters.AddWithValue("@TableName", tableName);
  440. using (var reader = cmd.ExecuteReader())
  441. {
  442. while (reader.Read())
  443. {
  444. string columnName = reader["COLUMN_NAME"].ToString();
  445. primaryKeys.Add(columnName);
  446. Console.WriteLine($"从sys视图找到主键列: {columnName}");
  447. }
  448. }
  449. }
  450. }
  451. Console.WriteLine($"主键获取完成,共找到{primaryKeys.Count}个主键列");
  452. }
  453. catch (Exception ex)
  454. {
  455. Console.WriteLine($"获取SQL Server主键信息时出错: {ex.Message}");
  456. Console.WriteLine($"错误堆栈: {ex.StackTrace}");
  457. }
  458. return primaryKeys;
  459. }
  460. static string GetMySqlDataType(Type sqlServerType)
  461. {
  462. if (sqlServerType == typeof(Guid) || sqlServerType == typeof(string))
  463. {
  464. return "VARCHAR(36)";
  465. }
  466. else if (sqlServerType == typeof(int))
  467. {
  468. return "INT";
  469. }
  470. else if (sqlServerType == typeof(DateTime))
  471. {
  472. return "DATETIME";
  473. }
  474. else if (sqlServerType == typeof(decimal))
  475. {
  476. return "DECIMAL(18, 2)";
  477. }
  478. else if (sqlServerType == typeof(bool))
  479. {
  480. return "BOOLEAN";
  481. }
  482. else
  483. {
  484. return "VARCHAR(255)";
  485. }
  486. }
  487. static List<string> GetAllSqlServerTables(SqlConnection conn)
  488. {
  489. var tables = new List<string>();
  490. using (var cmd = new SqlCommand("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_CATALOG = DB_NAME()", conn))
  491. using (var reader = cmd.ExecuteReader())
  492. {
  493. while (reader.Read())
  494. {
  495. tables.Add(reader["TABLE_NAME"].ToString());
  496. }
  497. }
  498. return tables;
  499. }
  500. static DataTable ReadTableFromSqlServer(SqlConnection conn, string tableName)
  501. {
  502. var dt = new DataTable();
  503. using (var cmd = new SqlCommand($"SELECT * FROM {tableName}", conn))
  504. using (var adapter = new SqlDataAdapter(cmd))
  505. {
  506. adapter.Fill(dt);
  507. }
  508. // 调试信息:检查返回的列
  509. if (tableName == "Sys_Menu")
  510. {
  511. Console.WriteLine($"Sys_Menu表返回的列数: {dt.Columns.Count}");
  512. Console.WriteLine("列名列表:");
  513. foreach (DataColumn column in dt.Columns)
  514. {
  515. Console.WriteLine($"- {column.ColumnName}");
  516. }
  517. }
  518. return dt;
  519. }
  520. static void ClearMySqlTable(MySqlConnection conn, string tableName)
  521. {
  522. // 禁用外键约束
  523. using (var cmd = new MySqlCommand("SET FOREIGN_KEY_CHECKS = 0", conn))
  524. {
  525. cmd.ExecuteNonQuery();
  526. }
  527. try
  528. {
  529. // 清空表
  530. using (var cmd = new MySqlCommand($"TRUNCATE TABLE {tableName}", conn))
  531. {
  532. cmd.ExecuteNonQuery();
  533. }
  534. }
  535. finally
  536. {
  537. // 启用外键约束
  538. using (var cmd = new MySqlCommand("SET FOREIGN_KEY_CHECKS = 1", conn))
  539. {
  540. cmd.ExecuteNonQuery();
  541. }
  542. }
  543. }
  544. static void WriteTableToMySql(MySqlConnection conn, string tableName, DataTable dataTable)
  545. {
  546. if (dataTable.Rows.Count == 0) return;
  547. Console.WriteLine($"开始写入{dataTable.Rows.Count}条记录到{tableName}表...");
  548. // 构建列名列表
  549. var columns = new List<string>();
  550. for (int i = 0; i < dataTable.Columns.Count; i++)
  551. {
  552. columns.Add(EscapeMySqlIdentifier(dataTable.Columns[i].ColumnName));
  553. }
  554. var columnList = string.Join(", ", columns);
  555. // 批量插入的大小
  556. const int batchSize = 1000;
  557. int totalRows = dataTable.Rows.Count;
  558. int processedRows = 0;
  559. using (var transaction = conn.BeginTransaction())
  560. {
  561. try
  562. {
  563. // 分批处理数据
  564. while (processedRows < totalRows)
  565. {
  566. // 计算当前批次的行数
  567. int currentBatchSize = Math.Min(batchSize, totalRows - processedRows);
  568. // 构建批量插入语句
  569. var insertSqlBuilder = new StringBuilder();
  570. insertSqlBuilder.AppendLine($"INSERT INTO {tableName} ({columnList}) VALUES");
  571. // 构建值列表
  572. var valuesList = new List<string>();
  573. for (int i = 0; i < currentBatchSize; i++)
  574. {
  575. var row = dataTable.Rows[processedRows + i];
  576. var valueBuilder = new StringBuilder("(");
  577. for (int j = 0; j < dataTable.Columns.Count; j++)
  578. {
  579. var value = row[j] == DBNull.Value ? null : row[j];
  580. if (value is System.Guid)
  581. {
  582. valueBuilder.Append($"'{value.ToString().Replace("'", "''")}'");
  583. }
  584. else if (value is string)
  585. {
  586. valueBuilder.Append($"'{value.ToString().Replace("'", "''")}'");
  587. }
  588. else if (value is DateTime)
  589. {
  590. valueBuilder.Append($"'{((DateTime)value).ToString("yyyy-MM-dd HH:mm:ss")}'");
  591. }
  592. else if (value is bool)
  593. {
  594. valueBuilder.Append(((bool)value) ? "1" : "0");
  595. }
  596. else if (value is byte[])
  597. {
  598. // 对于字节数组,使用NULL值
  599. valueBuilder.Append("NULL");
  600. }
  601. else if (value == null)
  602. {
  603. valueBuilder.Append("NULL");
  604. }
  605. else
  606. {
  607. valueBuilder.Append(value.ToString());
  608. }
  609. if (j < dataTable.Columns.Count - 1)
  610. {
  611. valueBuilder.Append(", ");
  612. }
  613. }
  614. valueBuilder.Append(")");
  615. valuesList.Add(valueBuilder.ToString());
  616. }
  617. insertSqlBuilder.AppendLine(string.Join(",\n", valuesList));
  618. string insertSql = insertSqlBuilder.ToString();
  619. // 执行批量插入
  620. using (var cmd = new MySqlCommand(insertSql, conn, transaction))
  621. {
  622. cmd.ExecuteNonQuery();
  623. }
  624. // 更新处理进度
  625. processedRows += currentBatchSize;
  626. // 显示进度
  627. if (processedRows % 10000 == 0 || processedRows == totalRows)
  628. {
  629. Console.WriteLine($"已处理{processedRows}/{totalRows}条记录...");
  630. }
  631. }
  632. transaction.Commit();
  633. Console.WriteLine($"成功写入{totalRows}条记录到{tableName}表");
  634. }
  635. catch (Exception ex)
  636. {
  637. transaction.Rollback();
  638. throw new Exception($"写入{tableName}表时出错: {ex.Message}", ex);
  639. }
  640. }
  641. }
  642. }
  643. class Config
  644. {
  645. public ConnectionStrings ConnectionStrings { get; set; }
  646. }
  647. class ConnectionStrings
  648. {
  649. public string SqlServerDataContext { get; set; }
  650. public string SqlServerCommonDataContext { get; set; }
  651. public string MySqlTugboatCommon { get; set; }
  652. public string MySqlLiandaTugboatMIS { get; set; }
  653. }
  654. }