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.
 
 
 
Tiobon.Web.Core/Tiobon.Core.OPS.Tool/View/Frm_DBCompare_Step3.cs

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
}