You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1122 lines
42 KiB
1122 lines
42 KiB
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<CompareTables>(File.ReadAllText(fname));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
MessageBox.Show(e.Message);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region 日志操作
|
|
private static List<string> m_LogsTemp = new List<string>();
|
|
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<string> m_Logs = new List<string>();
|
|
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();
|
|
/// <summary>
|
|
/// 相应事件
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
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();
|
|
}
|
|
/// <summary>
|
|
/// 数据比对
|
|
/// </summary>
|
|
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<string>();
|
|
|
|
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
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 得到需要修改的SQL语句
|
|
/// </summary>
|
|
/// <param name="tableName"></param>
|
|
/// <param name="keys"></param>
|
|
/// <param name="updateField"></param>
|
|
/// <returns></returns>
|
|
private static string GenerateWaitUpdateTSql(string tableName, IReadOnlyList<string> keys, IReadOnlyList<string> 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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 连接键
|
|
/// </summary>
|
|
/// <param name="keys">键集合</param>
|
|
/// <param name="dbName">数据库名称</param>
|
|
/// <returns></returns>
|
|
private static string UnionKey(IReadOnlyList<string> 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<string> primaryKeys, ICollection<string> ps, Guid? tenantId,
|
|
CompareParentMapper parentMapper, List<CompareForeignMapper> 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<Tuple<Guid?, string, string, Guid>> IdMappers = new List<Tuple<Guid?, string, string, Guid>>();
|
|
|
|
private StringBuilder GenerateAddSql(DataTable data, string tableName, Guid? tenantId,
|
|
CompareParentMapper parentMapper, List<CompareForeignMapper> foreignMappers)
|
|
{
|
|
var sql = new StringBuilder();
|
|
|
|
//1.初始化主键为新Id,并保留原Id映射关系
|
|
foreach (DataRow row in data.Rows)
|
|
{
|
|
var newGuid = Guid.NewGuid();
|
|
|
|
IdMappers.Add(new Tuple<Guid?, string, string, Guid>(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<Tuple<int, string, string, string>>();
|
|
|
|
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<int, string, string, string>(
|
|
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<string> primaryKeys, Guid? tenantId,
|
|
CompareParentMapper parentMapper, List<CompareForeignMapper> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建 update 和 delete 条件后的 where 语句
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private string BuildUpdateOrDeleteWhere(string tableName, DataRow row, IReadOnlyList<string> keys, Guid? tenantId,
|
|
CompareParentMapper parentMapper, List<CompareForeignMapper> 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;
|
|
}
|
|
/// <summary>
|
|
/// 连接两个键数据
|
|
/// </summary>
|
|
/// <param name="keys"></param>
|
|
/// <param name="row"></param>
|
|
/// <returns></returns>
|
|
private string UnionKey2Data(List<string> 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();
|
|
}
|
|
/// <summary>
|
|
/// 转换数据库类型
|
|
/// </summary>
|
|
/// <param name="data"></param>
|
|
/// <returns></returns>
|
|
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();
|
|
}
|
|
/// <summary>
|
|
/// 头部注释信息
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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();
|
|
}
|
|
/// <summary>
|
|
/// 键描述信息
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private static string KeyComment(List<string> 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();
|
|
}
|
|
/// <summary>
|
|
/// 插入测试
|
|
/// </summary>
|
|
/// <param name="data"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
/// <summary>
|
|
/// 清理垃圾数据
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private string CleanRubbish(string tableName, IReadOnlyList<CleanRubbishMapper> 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 拷贝
|
|
/// <summary>
|
|
/// 拷贝比对后的SQL语句
|
|
/// </summary>
|
|
/// <param name="sender"></param>
|
|
/// <param name="e"></param>
|
|
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
|
|
{
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public string TableName { get; set; }
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public List<string> Insert { get; set; }
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public List<string> Update { get; set; }
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public List<string> UpdateFiled { get; set; }
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public List<string> Delete { get; set; }
|
|
/// <summary>
|
|
/// 清理垃圾字段
|
|
/// </summary>
|
|
public List<CleanRubbishMapper> CleanRubbish { set; get; }
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public string Filter { get; set; }
|
|
|
|
/// <summary>
|
|
/// 是否租户隔离
|
|
/// </summary>
|
|
public string HaveTenant { get; set; }
|
|
|
|
/// <summary>
|
|
/// 父节点映射
|
|
/// </summary>
|
|
public CompareParentMapper ParentMapper { get; set; }
|
|
|
|
/// <summary>
|
|
/// 外键映射
|
|
/// </summary>
|
|
public List<CompareForeignMapper> ForeignMapper { get; set; }
|
|
}
|
|
|
|
public class CleanRubbishMapper
|
|
{
|
|
/// <summary>
|
|
/// 清理比较字段
|
|
/// </summary>
|
|
public string CleanRubbishFiled { get; set; }
|
|
|
|
/// <summary>
|
|
/// 清理比较字段所属表
|
|
/// </summary>
|
|
public string CleanRubbishTable { get; set; }
|
|
|
|
/// <summary>
|
|
/// 清理比较字段所属表字段
|
|
/// </summary>
|
|
public string CleanRubbishTableFiled { get; set; }
|
|
}
|
|
|
|
public class CompareParentMapper
|
|
{
|
|
/// <summary>
|
|
/// 父节点字段
|
|
/// </summary>
|
|
public string ParentFiled { get; set; }
|
|
|
|
/// <summary>
|
|
/// 查找字段
|
|
/// </summary>
|
|
public string SearchFiled { get; set; }
|
|
}
|
|
|
|
public class CompareForeignMapper
|
|
{
|
|
/// <summary>
|
|
/// 外键表名
|
|
/// </summary>
|
|
public string ForeignTable { get; set; }
|
|
|
|
/// <summary>
|
|
/// 外键字段名
|
|
/// </summary>
|
|
public string ForeignKey { get; set; }
|
|
|
|
/// <summary>
|
|
/// 查找字段
|
|
/// </summary>
|
|
public string SearchFiled { get; set; }
|
|
}
|
|
|
|
public class CompareTables
|
|
{
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public List<CompareTablesItem> Tables { get; set; }
|
|
}
|
|
#endregion
|
|
}
|
|
|