using Tiobon.Core.OPS.Tool.OPS.Tool.Helper; using MySql.Data.MySqlClient; using System; using System.Collections.Generic; using System.Data; using System.IO; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading.Tasks; using System.Threading; namespace Tiobon.Core.OPS.Tool.OPS.Tool.View { public partial class Frm_DBCompare_Step3 : Form { #region 初始化 string m_ConnStr = ""; string m_Version = ""; string m_HdisConnStr = ""; SSHHelper sSH; bool isLinux = Const.config.system == "Linux"; string installpath = Const.config.install_item_dir; public Frm_DBCompare_Step3(string hdisConnStr, string connStr, string ver, SSHHelper sSH, FormWindowState windowState) { InitializeComponent(); m_ConnStr = connStr; m_HdisConnStr = hdisConnStr; m_Version = ver; this.sSH = sSH; LoadConf(); timer_Main.Start(); WindowState = windowState; this.txt_fname.Text = $"{Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory)}\\系统数据比对结果_{DateTime.Now.ToString("yyyy_MM_dd")}.sql"; } bool m_Stop = false; bool b_Closed = false; private void Frm_DBCompare_Step3_FormClosing(object sender, FormClosingEventArgs e) { if (b_Closed) return; m_Stop = true; if (isLinux) { try { //写入数据库版本 this.sSH.Excute_Longcmd_NoError($"sudo echo {m_Version} > /home/{sSH.user}/ihdis/produse/DbVersion"); } catch { } bool isRestart = MessageBox.Show("是否重新启动服务!", "提示信息!", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes; if (isRestart) { try { string tempCommand = $"cd /home/{sSH.user}/ihdis/produse;sudo docker-compose docker-compose.yml restart"; Task.Factory.StartNew(() => { this.sSH.Excute_Longcmd_NoError(tempCommand); }); } catch { } } } else { bool isRestart = MessageBox.Show("是否重新启动服务!", "提示信息!", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.Yes; if (isRestart) { CmdHelper.RunCmd($"{installpath.Split(':')[0].Trim()}: \r\ncd {$"{installpath}/"}" + $"\r\ndocker-compose down" + $"\r\n", out _); Thread.Sleep(1000 * 10); CmdHelper.RunCmd($"{installpath.Split(':')[0].Trim()}: \r\ncd {$"{installpath}/"}" + $"\r\ndocker-compose up -d \r\nexit", out _); Thread.Sleep(1000 * 10); } } } #endregion #region 加载配置项 CompareTables m_CompareTablesConf; private void LoadConf() { try { string fname = AppDomain.CurrentDomain.BaseDirectory + @"Config\CompareConfig.json"; if (File.Exists(fname)) m_CompareTablesConf = Newtonsoft.Json.JsonConvert.DeserializeObject(File.ReadAllText(fname)); } catch (Exception e) { MessageBox.Show(e.Message); } } #endregion #region 日志操作 private static List m_LogsTemp = new List(); private void timer_Main_Tick(object sender, EventArgs e) { try { lock (m_Logs) { m_LogsTemp.Clear(); m_Logs.ForEach(log => m_LogsTemp.Add(log)); m_Logs.Clear(); } if (m_LogsTemp.Count > 0) { if (this.lb_Logger.Items.Count > 2000) this.lb_Logger.Items.Clear(); m_LogsTemp.ForEach(log => this.lb_Logger.Items.Add(log)); this.lb_Logger.TopIndex = this.lb_Logger.Items.Count - 1; } lock (m_LockContent) { if (!string.IsNullOrEmpty(m_Content)) { if (m_Content == "clear") { this.rtb_Sql.Text = string.Empty; } else { this.rtb_Sql.Text = m_Content; } m_Content = string.Empty; } } lock (m_LockProgressBar) { progressBar_Main.Maximum = m_PBMax; progressBar_Main.Value = m_PBValue; } } catch { } } private static List m_Logs = new List(); private void SendLog(string oprator, string msg) { lock (m_Logs) { Const.write_log($"[{oprator}] {msg}"); if (string.IsNullOrEmpty(oprator)) m_Logs.Add($"{DateTime.Now.ToString("HH:mm:ss")} {msg}"); else m_Logs.Add($"{DateTime.Now.ToString("HH:mm:ss")} [{oprator}] {msg}"); } } #endregion #region 退出 bool b_save = false; private void btn_Quit_Click(object sender, EventArgs e) { //if (!b_save) //{ // if (!string.IsNullOrEmpty(this.rtb_Sql.Text)) // { // MessageBox.Show("比对已完成,但是结果未保存或复制!", "提示"); // b_save = true; // return; // } //} this.Close(); } #endregion #region 执行比对 private static readonly string EmptyGuid = "00000000-0000-0000-0000-000000000000"; string m_Content = string.Empty; object m_LockContent = new object(); bool b_In = false; object m_LockIn = new object(); int m_PBMax; int m_PBValue; object m_LockProgressBar = new object(); /// /// 相应事件 /// /// /// private void btn_Compare_Click(object sender, EventArgs e) { lock (m_LockIn) { if (b_In) { return; } b_In = true; } if (m_CompareTablesConf == null || m_CompareTablesConf.Tables == null || m_CompareTablesConf.Tables.Count == 0) { MessageBox.Show($"没有需要比对的表,请检查配置文件[\\Config\\CompareConfig.json]", "提示"); } new System.Threading.Thread(Compare).Start(); } /// /// 数据比对 /// private void Compare() { var sum = 0; var sb = new StringBuilder(); try { var bHasContent = false; SendLog("", "开始比对数据"); // 添加注释头 sb.AppendLine("SET FOREIGN_KEY_CHECKS = 0;\r\n"); sum = m_CompareTablesConf.Tables.Count; lock (m_LockProgressBar) { m_PBMax = sum; } var index = 0; //1.查询hdis所有租户 const string tenantQuery = "select * from SystemTenant"; var tenantDs = MySqlHelper.ExecuteDataset(m_HdisConnStr, tenantQuery); //清理id映射表 IdMappers.Clear(); //2.循环所有表 m_CompareTablesConf.Tables.ForEach(t => { SendLog("", $"比对表 {t.TableName} 开始"); if (t.HaveTenant.Equals("true")) { //3.循环所有租户数据处理 for (var i = 0; i < tenantDs.Tables[0].Rows.Count; i++) { var tenant = tenantDs.Tables[0].Rows[i]; var tenantId = tenant[0].ToString(); Guid.TryParse(tenantId, out var tenantGuid); ExecCompare(t, sb, ref bHasContent, tenantGuid); } } else { ExecCompare(t, sb, ref bHasContent); } SendLog("", $"比对表 {t.TableName} 结束"); lock (m_LockProgressBar) { m_PBValue = index++; } }); sb.AppendLine("SET FOREIGN_KEY_CHECKS = 1;\r\n"); if (m_Stop) return; b_save = false; if (bHasContent) { lock (m_LockContent) m_Content = sb.ToString(); SendLog("", "比较数据完毕"); HeaderComment(); // 添加注释 } else { SendLog("", "比较数据完毕,数据完全一致"); lock (m_LockContent) m_Content = "clear"; } } catch (Exception ex) { SendLog("", $"比对失败:{ex}"); } finally { lock (m_LockIn) { b_In = false; } lock (m_LockProgressBar) { m_PBMax = sum; m_PBValue = sum; } } } private void ExecCompare(CompareTablesItem t, StringBuilder sb, ref bool hasContent, Guid? tenantId = null) { #region step 1 比对数据-删除 var ps = new List(); var tenantWhere = string.Empty; if (t.Delete != null && t.Delete.Any()) { if (tenantId.HasValue) tenantWhere = $" AND TenantId = '{tenantId}'"; //1.1查询待删除的数据 var sql = $"SELECT s.* FROM `hdis`.`{t.TableName}` s WHERE {UnionKey(t.Insert, "s")} " + $"NOT IN (SELECT {UnionKey(t.Insert, string.Empty)} FROM `hdis_compare`.`{t.TableName}` ) {tenantWhere};"; SendLog("删除", $"{sql}"); var ds = MySqlHelper.ExecuteDataset(m_ConnStr, sql); if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0) { SendLog("", $"{t.TableName} 待删除数据量 {ds.Tables[0].Rows.Count}"); sb.AppendLine($"/* DELTE DATA {ds.Tables[0].Rows.Count} ROWS, DELETE KEYS: {KeyComment(t.Delete)} */"); //1.2生成待删除的数据 var sbContent = GenerateRemoveSql(ds.Tables[0], t.TableName, t.Delete, ps, tenantId, t.ParentMapper, t.ForeignMapper); if (sbContent.Length > 0) { hasContent = true; sb.Append(sbContent); } } else { SendLog("", $"{t.TableName} 没有待删除数据"); } } else { SendLog("", $"{t.TableName} 无需删除数据"); } #endregion #region step 2 比对数据-新增 SendLog("", $"比对表 {t.TableName} 开始"); if (t.Insert != null && t.Insert.Any()) { if (tenantId.HasValue) tenantWhere = $" WHERE TenantId = '{tenantId}'"; var orderSql = string.Empty; if (!string.IsNullOrWhiteSpace(t.ParentMapper?.ParentFiled) && t.ParentMapper.ParentFiled == "ParentId") { orderSql = " Order by s.ParentId"; } //2.1查询待新增的数据 var sql = $"SELECT s.* FROM `hdis_compare`.`{t.TableName}` s WHERE {UnionKey(t.Insert, "s")} " + $"NOT IN (SELECT {UnionKey(t.Insert, string.Empty)} FROM `hdis`.`{t.TableName}` {tenantWhere}) {orderSql};"; SendLog("新增", $"{sql}"); var ds = MySqlHelper.ExecuteDataset(m_ConnStr, sql); if (ds != null && ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0) { SendLog("", $"{t.TableName} 待新增数据量 {ds.Tables[0].Rows.Count}"); sb.AppendLine($"/* INSERT DATA {ds.Tables[0].Rows.Count} ROWS. INSERT KEYS: {KeyComment(t.Insert)} */"); //2.2生成待新增的数据 var sbContent = GenerateAddSql(ds.Tables[0], t.TableName, tenantId, t.ParentMapper, t.ForeignMapper); if (sbContent.Length > 0) { hasContent = true; sb.Append(sbContent); } } else { SendLog("", $"{t.TableName} 没有待新增数据"); } } else { SendLog("", $"{t.TableName} 无需新增数据"); } #endregion #region step 3 比对数据-修改 if (t.Update != null && t.Update.Any()) { if (t.UpdateFiled.Count > 0) { //3.1查询待修改的sql语句,查询待修改数据 var sql = GenerateWaitUpdateTSql(t.TableName, t.Update, t.UpdateFiled, tenantId); // 待修改的数据,即是拿ihdis_compare 与 ihdis 比较,同一张表A 中,ihdis_compare数据库中的表A 与 ihdis数据库中的表A 中的数据会分为两类 SendLog("修改", $"{sql}"); // 1,ihdis_compare[A] 中的数据比 ihdis[A] 中多的部分 var dt = new DataTable(); // 2,ihdis_compare[A] 中的数据与 ihdis[A] 中相同的部分 dt.Load(MySqlHelper.ExecuteReader(m_ConnStr, sql)); // 3, ihdis_compare[A] 中的数据比 ihdis[A] 中少的部分 if (dt.Rows.Count > 0) sb.AppendLine($"/* UPDATE DATA {dt.Rows.Count} ROWS, UPDATE KEYS: {KeyComment(t.Update)} UPDATE FIELD: {KeyComment(t.UpdateFiled)} */"); //3.2生成修改sql语句 var sbContent = GenerateModifySql(dt, t.TableName, t.Update, tenantId, t.ParentMapper, t.ForeignMapper); if (sbContent.Length > 0) { hasContent = true; sb.Append(sbContent); } } else { SendLog("", $"{t.TableName} 没有待修改的数据"); } } else { SendLog("", $"{t.TableName} 无需修改数据"); } #endregion #region step 4 清理垃圾数据 if (t.CleanRubbish != null && t.CleanRubbish.Count > 0) // 配置了清理垃圾数据的列 { sb.AppendLine($"/* CLEAR RUBBISH DATA , CLEAN KEYS: {KeyComment(t.CleanRubbish.Select(x => x.CleanRubbishFiled).ToList())} */"); var temp = CleanRubbish(t.TableName, t.CleanRubbish, tenantId); if (!string.IsNullOrEmpty(temp)) { hasContent = true; sb.AppendLine(temp); } SendLog($"", $"清理垃圾:{temp}"); } #endregion } /// /// 得到需要修改的SQL语句 /// /// /// /// /// private static string GenerateWaitUpdateTSql(string tableName, IReadOnlyList keys, IReadOnlyList updateField, Guid? tenantId) { if (keys == null || keys.Count <= 0 || updateField == null || updateField.Count <= 0) return string.Empty; var sb = new StringBuilder(); var temp = keys.Union(updateField).ToList(); sb.Append($"SELECT"); for (var i = 0; i < temp.Count; i++) { sb.Append($" p.`{temp[i]}` "); if (i != temp.Count - 1) sb.Append(" , "); } sb.Append($" FROM hdis.`{tableName}` s, hdis_compare.`{tableName}` p WHERE "); for (var i = 0; i < keys.Count; i++) { sb.Append($" s.`{keys[i]}` = p.`{keys[i]}` "); if (i != keys.Count - 1) sb.Append(" AND "); } sb.Append(" AND ( "); for (var i = 0; i < updateField.Count; i++) { sb.Append($"s.`{updateField[i]}` != p.`{updateField[i]}`"); if (i != updateField.Count - 1) sb.Append(" OR "); } sb.Append(" ) "); if (tenantId.HasValue) { sb.Append($" AND s.`TenantId` = '{tenantId}' "); } return sb.ToString(); } /// /// 连接键 /// /// 键集合 /// 数据库名称 /// private static string UnionKey(IReadOnlyList keys, string dbName) { if (keys == null || keys.Count <= 0) return string.Empty; //如果键集合为空,直接返回空字符串 var sb = new StringBuilder(); sb.Append("concat("); for (var i = 0; i < keys.Count; ++i) { var temp = $"{(string.IsNullOrEmpty(dbName) ? string.Empty : $"{dbName}.")}`{keys[i]}`"; sb.Append($"IFNULL({temp},'')"); if (i != keys.Count - 1) { sb.Append(","); } } sb.Append(")"); return sb.ToString(); } private StringBuilder GenerateRemoveSql(DataTable data, string tableName, List primaryKeys, ICollection ps, Guid? tenantId, CompareParentMapper parentMapper, List foreignMappers) { var sql = new StringBuilder(); foreach (DataRow row in data.Rows) { var key = UnionKey2Data(primaryKeys, row); ps.Add(key); sql.AppendLine($"DELETE FROM `hdis`.`{tableName}` WHERE {BuildUpdateOrDeleteWhere(tableName, row, primaryKeys, tenantId, parentMapper, foreignMappers)};"); sql.AppendLine(); } return sql; } //新老主键映射关系 public readonly List> IdMappers = new List>(); private StringBuilder GenerateAddSql(DataTable data, string tableName, Guid? tenantId, CompareParentMapper parentMapper, List foreignMappers) { var sql = new StringBuilder(); //1.初始化主键为新Id,并保留原Id映射关系 foreach (DataRow row in data.Rows) { var newGuid = Guid.NewGuid(); IdMappers.Add(new Tuple(tenantId, tableName, row["Id"].ToString(), newGuid)); row["Id"] = newGuid; } foreach (DataRow row in data.Rows) { var temp = InsertComment(row, tableName, "id"); if (!string.IsNullOrEmpty(temp)) { sql.AppendLine(temp); } int? tenantColumnIndex = null; int? parentIdIndex = null; var foreignIndexMappers = new List>(); sql.Append($"{(!string.IsNullOrEmpty(temp) ? "/*" : string.Empty)}INSERT INTO `hdis`.`{tableName}` ("); for (var i = 0; i < data.Columns.Count; i++) { //2.涉及递归字段与跨表问题,记录字段索引 var filedName = data.Columns[i].ToString(); if (filedName == parentMapper?.ParentFiled) { parentIdIndex = i;//父节点字段索引 } if (filedName == "TenantId") { tenantColumnIndex = i; //租户字段索引 } var foreignFiled = foreignMappers.FirstOrDefault(x => x.ForeignKey == filedName); if (foreignFiled != null) { foreignIndexMappers.Add(new Tuple( i, foreignFiled.ForeignTable, foreignFiled.ForeignKey, foreignFiled.SearchFiled));//外键字段索引 } if (i != 0) sql.Append(" "); sql.Append($"`{data.Columns[i]}`"); if (i != data.Columns.Count - 1) sql.Append(","); } sql.Append($") VALUES ("); for (var i = 0; i < row.ItemArray.Length; i++) { //3.涉及递归字段与跨表问题,规则获取 var rowValue = row.ItemArray[i].ToString(); if (tenantColumnIndex.HasValue && i == tenantColumnIndex) { sql.Append($"'{tenantId}'"); } else if (parentIdIndex.HasValue && i == parentIdIndex) { //从本地或数据库查询父节点ID var parentId = GetParentId(tableName, parentMapper.SearchFiled, rowValue, tenantId); sql.Append($"'{parentId}'"); } else if (foreignIndexMappers.Select(x => x.Item1).Contains(i)) { var foreign = foreignIndexMappers.FirstOrDefault(x => x.Item1 == i); //根据表名到Compare库查Name,在到HDIS库查询外键ID var foreignId = GetForeignId(foreign?.Item2, foreign?.Item4, rowValue, tenantId); sql.Append($"'{foreignId}'"); } else { sql.Append($"{GetSqlDataByType(row.ItemArray[i])}"); } if (i != data.Columns.Count - 1) sql.Append(","); } sql.Append($");{(!string.IsNullOrEmpty(temp) ? "*/" : string.Empty)}\r\n"); sql.AppendLine(); } return sql; } private string GetParentId(string tabName, string filedName, string parentFiledValue, Guid? tenantId) { //1.先从本地关系表找上级id Guid parentId; if (EmptyGuid == parentFiledValue) return EmptyGuid; var idMapper = IdMappers?.FirstOrDefault(x => x.Item1 == tenantId && x.Item2 == tabName && x.Item3 == parentFiledValue); if (idMapper != null) { return idMapper.Item4.ToString(); } //2.从hdis库找上级id var tenantCondition = string.Empty; if (tenantId.HasValue) { tenantCondition += $"AND `TenantId`= '{tenantId}'"; } var sql = $"SELECT s.Id FROM `hdis`.`{tabName}` s WHERE `{filedName}` = (SELECT `{filedName}` FROM `hdis_compare`.`{tabName}` " + $"where `Id`= '{parentFiledValue}') {tenantCondition}; "; var result = MySqlHelper.ExecuteScalar(m_ConnStr, sql); return result?.ToString(); } private string GetForeignId(string foreignTableName, string searchFiledName, string filedValue, Guid? tenantId) { //1.先从本地关系表找外键id var idMapper = IdMappers?.FirstOrDefault(x => x.Item1 == tenantId && x.Item2 == foreignTableName && x.Item3 == filedValue); if (idMapper != null) { return idMapper.Item4.ToString(); } //2.从hdis库找外键id var tenantCondition = string.Empty; if (tenantId.HasValue) { tenantCondition += $"AND s.`TenantId`= '{tenantId}'"; } var sql = $"SELECT s.Id FROM `hdis`.`{foreignTableName}` s WHERE s.`{searchFiledName}` = " + $"(SELECT `{searchFiledName}` FROM `hdis_compare`.`{foreignTableName}` where `Id`= '{filedValue}') {tenantCondition}; "; var result = MySqlHelper.ExecuteScalar(m_ConnStr, sql); return result?.ToString(); } private StringBuilder GenerateModifySql(DataTable data, string tableName, IReadOnlyList primaryKeys, Guid? tenantId, CompareParentMapper parentMapper, List foreignMappers) { var sql = new StringBuilder(); foreach (DataRow row in data.Rows) { sql.Append($"UPDATE `hdis`.`{tableName}` SET "); for (var i = 0; i < data.Columns.Count; i++) { if (i != 0) sql.Append(" "); var columnName = data.Columns[i].ToString(); var columnValue = row.ItemArray[i].ToString(); //根据父节点映射更新值 if (!string.IsNullOrWhiteSpace(parentMapper?.ParentFiled) && parentMapper.ParentFiled == columnName) { columnValue = GetParentId(tableName, parentMapper.SearchFiled, columnValue, tenantId); sql.Append($"`{data.Columns[i]}` = '{columnValue}' "); } //根据外键映射更新值 else if (foreignMappers.Any(x => x.ForeignKey == columnName)) { var foreignMapper = foreignMappers.FirstOrDefault(x => x.ForeignKey == columnName); columnValue = GetForeignId(foreignMapper?.ForeignTable, foreignMapper?.SearchFiled, columnValue, tenantId); sql.Append($"`{data.Columns[i]}` = '{columnValue}' "); } else { sql.Append($"`{data.Columns[i]}` = {GetSqlDataByType(row.ItemArray[i])} "); } if (i != data.Columns.Count - 1) sql.Append(","); } sql.Append($" WHERE {BuildUpdateOrDeleteWhere(tableName, row, primaryKeys, tenantId, parentMapper, foreignMappers)};\r\n"); sql.AppendLine(); } return sql; } /// /// 创建 update 和 delete 条件后的 where 语句 /// /// private string BuildUpdateOrDeleteWhere(string tableName, DataRow row, IReadOnlyList keys, Guid? tenantId, CompareParentMapper parentMapper, List foreignMappers) { var temp = string.Empty; if (keys == null || keys.Count <= 0) return temp; for (var i = 0; i < keys.Count; i++) { var whereFiled = keys[i]; var whereValue = row[whereFiled].ToString(); //映射父节点条件值 if (!string.IsNullOrWhiteSpace(parentMapper?.ParentFiled) && parentMapper.ParentFiled == whereFiled) { temp += $"`{whereFiled}` = '{GetParentId(tableName, parentMapper.SearchFiled, whereValue, tenantId)}' "; } //根据外键映射更新值 else if (foreignMappers.Any(x => x.ForeignKey == whereFiled)) { var foreignMapper = foreignMappers.FirstOrDefault(x => x.ForeignKey == whereFiled); whereValue = GetForeignId(foreignMapper?.ForeignTable, foreignMapper?.SearchFiled, whereValue, tenantId); temp += $"`{whereFiled}` = '{whereValue}' "; } else { temp += $" `{whereFiled}` = {GetSqlDataByType(whereValue)} "; } if (i != keys.Count - 1) temp += " AND "; } if (tenantId.HasValue) { temp += $" AND `TenantId` = '{tenantId}'"; } return temp; } /// /// 连接两个键数据 /// /// /// /// private string UnionKey2Data(List keys, DataRow row) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < keys.Count; ++i) { sb.Append($"{GetSqlDataByType(row[keys[i]])}"); if (i != keys.Count - 1) { sb.Append(""); } } return sb.ToString(); } /// /// 转换数据库类型 /// /// /// private string GetSqlDataByType(object data) { string typeName = data.GetType().Name; if (typeName == "DBNull") return "NULL"; if (typeName == "Guid" || typeName == "String" || typeName == "DateTime") { if (data.ToString().Length == 19) { try { DateTime dt = DateTime.ParseExact(data.ToString(), "MM/dd/yyyy HH:mm:ss", null, new System.Globalization.DateTimeStyles()); data = dt.ToString("yyyy/MM/dd HH:mm:ss"); } catch { } } else // 是否为字符类型 { string value = data.ToString(); if (value.IndexOf("\r") > 0) value = value.Replace("\r", "\\r"); if (value.IndexOf("\n") > 0) value = value.Replace("\n", "\\n"); if (value.IndexOf("'") > 0) value = value.Replace("'", "''"); if (value.IndexOf("\\") > 0) value = value.Replace("\\", "\\\\"); data = value; } return $"'{data}'"; } return data.ToString(); } /// /// 头部注释信息 /// /// private string HeaderComment() { MySqlConnectionStringBuilder con = new MySqlConnectionStringBuilder(m_ConnStr); StringBuilder builder = new StringBuilder(); SendLog("", "-- MySQL Server DataComapare Result"); SendLog("", "--"); SendLog("", $"-- Host: {con.Server} Database: {con.Database}"); SendLog("", "--------------------------------------------------------"); SendLog("", "-- Server version 5.7.25"); SendLog("", ""); SendLog("", "-- 脚本执行注意事项:"); SendLog("", "-- (1) 表中数据的比对若不是选择主键列为比对依据,则生成的插入数据中可能会会产生错误:"); SendLog("", "-- Duplicate entry for key 'PRIMARY',此时应根据提示找到该行数据,根据实际需要判断是否插入或不插入该行数据。"); SendLog("", "-- (2) 若配置文件已经配置删除某表的数据,请注意浏览脚本,再次确定是否需要删除该数据。"); SendLog("", ""); return builder.ToString(); } /// /// 键描述信息 /// /// private static string KeyComment(List keys) { if (keys == null || keys.Count <= 0) return string.Empty; var builder = new StringBuilder(); for (var i = 0; i < keys.Count; i++) { builder.Append(keys[i]); if (i != keys.Count - 1) builder.Append(","); } return builder.ToString(); } /// /// 插入测试 /// /// /// private string InsertComment(DataRow data, string tableName, string primaryKey) { if (data == null || string.IsNullOrEmpty(primaryKey)) return string.Empty; var sql = $"select count(1) from hdis.`{tableName}` where `{primaryKey}` = {GetSqlDataByType(data[primaryKey])}"; var count = 0; try { count = int.Parse(MySqlHelper.ExecuteScalar(m_ConnStr, sql).ToString()); } catch (Exception error) { SendLog("", error.ToString()); } return count > 0 ? $"/* PRIMARY KEY 为 {data[primaryKey]} 的该行数据在 hdis 数据库中已经存在,插入数据可能会报错。 */ " : string.Empty; } /// /// 清理垃圾数据 /// /// private string CleanRubbish(string tableName, IReadOnlyList cleanMappers, Guid? tenantId) { if (string.IsNullOrEmpty(tableName) || cleanMappers == null | !cleanMappers.Any()) return string.Empty; var builder = new StringBuilder(); builder.Append($"DELETE FROM hdis.`{tableName}` WHERE "); for (var i = 0; i < cleanMappers.Count; i++) { var cleanMapper = cleanMappers[i]; builder.Append($"(`{cleanMapper.CleanRubbishFiled}` IS NOT NULL AND `{cleanMapper.CleanRubbishFiled}` " + $"NOT IN (SELECT `{cleanMapper.CleanRubbishTableFiled}` FROM hdis.`{cleanMapper.CleanRubbishTable}`))"); if (i < cleanMappers.Count - 1) builder.Append(" AND "); } if (tenantId.HasValue) { builder.Append($" AND TenantId = '{tenantId}'"); } builder.Append(";\r\n"); var count = int.Parse(MySqlHelper.ExecuteScalar(m_ConnStr, builder.ToString().Replace("DELETE ", "SELECT COUNT(*) ")).ToString()); return count > 0 ? builder.ToString() : string.Empty; } #endregion #region 拷贝 /// /// 拷贝比对后的SQL语句 /// /// /// private void ll_Copy_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { if (string.IsNullOrEmpty(this.rtb_Sql.Text)) { return; } try { b_save = true; Clipboard.SetDataObject(this.rtb_Sql.Text.Trim(), true); MessageBox.Show("拷贝成功!", "提示"); } catch (Exception ex) { MessageBox.Show(ex.Message); } } #endregion #region 保存至文件 private void btn_select_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(this.rtb_Sql.Text)) { return; } try { string fname = ""; using (FolderBrowserDialog path = new FolderBrowserDialog()) { if (path.ShowDialog() == DialogResult.OK) { fname = $"{path.SelectedPath}\\ihdis_data_{DateTime.Now.ToString("yyyy_MM_dd")}.sql"; if (System.IO.File.Exists(fname)) { if (MessageBox.Show("文件已存在,是否覆盖?", "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No) { return; } } } } if (!string.IsNullOrEmpty(fname)) { //System.IO.File.WriteAllText(fname, this.rtb_Sql.Text, Encoding.UTF8); var utf8WithBom = new System.Text.UTF8Encoding(false); // 用true来指定包含bom using (StreamWriter sw = new StreamWriter(fname, false, utf8WithBom)) { sw.Write(this.rtb_Sql.Text); } MessageBox.Show($"文件保存成功:\r\n{fname}", "提示"); b_save = true; } } catch (Exception ex) { MessageBox.Show($"文件保存失败:{ex.ToString()}"); } } #endregion #region 复制日志 private void 复制ToolStripMenuItem_Click(object sender, EventArgs e) { Clipboard.SetDataObject(this.lb_Logger.SelectedItem); } #endregion #region 执行sql bool b_execsql = false; private void Frm_DBCompare_Step3_KeyDown(object sender, KeyEventArgs e) { if (b_execsql) return; b_execsql = true; if (e.KeyCode == Keys.F5) { if (!string.IsNullOrEmpty(this.rtb_Sql.Text) && MessageBox.Show($"是否确定执行sql语句?", "提示", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { try { int res = MySqlHelper.ExecuteNonQuery(m_ConnStr.Replace("jlmedcompare", "jlmed"), this.rtb_Sql.Text); SendLog("执行sql", $"sql语句执行完毕 {res}"); } catch (Exception ex) { SendLog("执行sql", $"sql语句执行失败 {ex.ToString()}"); } } } b_execsql = false; } #endregion } #region 配置文件 public class CompareTablesItem { /// /// /// public string TableName { get; set; } /// /// /// public List Insert { get; set; } /// /// /// public List Update { get; set; } /// /// /// public List UpdateFiled { get; set; } /// /// /// public List Delete { get; set; } /// /// 清理垃圾字段 /// public List CleanRubbish { set; get; } /// /// /// public string Filter { get; set; } /// /// 是否租户隔离 /// public string HaveTenant { get; set; } /// /// 父节点映射 /// public CompareParentMapper ParentMapper { get; set; } /// /// 外键映射 /// public List ForeignMapper { get; set; } } public class CleanRubbishMapper { /// /// 清理比较字段 /// public string CleanRubbishFiled { get; set; } /// /// 清理比较字段所属表 /// public string CleanRubbishTable { get; set; } /// /// 清理比较字段所属表字段 /// public string CleanRubbishTableFiled { get; set; } } public class CompareParentMapper { /// /// 父节点字段 /// public string ParentFiled { get; set; } /// /// 查找字段 /// public string SearchFiled { get; set; } } public class CompareForeignMapper { /// /// 外键表名 /// public string ForeignTable { get; set; } /// /// 外键字段名 /// public string ForeignKey { get; set; } /// /// 查找字段 /// public string SearchFiled { get; set; } } public class CompareTables { /// /// /// public List Tables { get; set; } } #endregion }