You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用C#对比DB2与SQL Server库存表并同步差异数据

C#实现DB2与SQL Server库存数据对比、校验及同步

一、数据对比:筛选差异记录

你已获取DB2和SQL Server的库存数据,接下来可通过**库存编码(PARCAKOD/STOK_KODU)**匹配,筛选两类差异记录:

  1. DB2存在但SQL Server不存在的新增记录
  2. 编码存在但其他字段(名称、单位等)不一致的更新记录

最高效的方式是先将SQL Server的库存编码存入HashSet实现快速查询,再通过Linq对比字段差异:

// 将SQL Server库存编码存入HashSet,用于快速判断是否存在
var sqlStockCodes = new HashSet<string>(sqlstok.Tables[0].AsEnumerable()
    .Select(row => row.Field<string>("STOK_KODU")));

// 筛选DB2中SQL Server不存在的新增记录
var newRecords = db2stok.Tables[0].AsEnumerable()
    .Where(row => !sqlStockCodes.Contains(row.Field<string>("PARCAKOD")))
    .CopyToDataTable();

// 筛选编码存在但字段不一致的更新记录
var updateRecords = db2stok.Tables[0].AsEnumerable()
    .Where(row => sqlStockCodes.Contains(row.Field<string>("PARCAKOD")))
    .Join(sqlstok.Tables[0].AsEnumerable(),
        db2Row => db2Row.Field<string>("PARCAKOD"),
        sqlRow => sqlRow.Field<string>("STOK_KODU"),
        (db2Row, sqlRow) => new { DB2Row = db2Row, SQLRow = sqlRow })
    .Where(join => 
        join.DB2Row.Field<string>("ADI") != join.SQLRow.Field<string>("ADI")
        || join.DB2Row.Field<string>("BIRIM") != join.SQLRow.Field<string>("BIRIM")
        || join.DB2Row.Field<decimal>("KDVYUZ") != join.SQLRow.Field<decimal>("KDVYUZ")
        || join.DB2Row.Field<string>("GRUP1") != join.SQLRow.Field<string>("GRUP1"))
    .Select(join => join.DB2Row)
    .CopyToDataTable();

// 合并所有差异记录到DataView
var diffTable = new DataTable();
diffTable.Merge(newRecords);
diffTable.Merge(updateRecords);
var diffView = new DataView(diffTable);

// 绑定到网格展示差异
metroGrid1.DataSource = diffView;
metroLabel1.Text = diffView.Count.ToString();

二、差异记录校验

同步前需校验数据合法性,避免脏数据入库,可根据业务规则添加校验逻辑:

bool ValidateRecord(DataRow row)
{
    // 校验必填字段非空
    if (string.IsNullOrEmpty(row.Field<string>("PARCAKOD")) || string.IsNullOrEmpty(row.Field<string>("ADI")))
    {
        MessageBox.Show($"无效记录:库存编码或名称为空,编码[{row.Field<string>("PARCAKOD")}]");
        return false;
    }
    // 校验KDV百分比范围(0-100)
    if (row.Field<decimal>("KDVYUZ") < 0 || row.Field<decimal>("KDVYUZ") > 100)
    {
        MessageBox.Show($"无效记录:KDV百分比超出范围,编码[{row.Field<string>("PARCAKOD")}],值[{row.Field<decimal>("KDVYUZ")}]");
        return false;
    }
    // 可添加其他自定义校验规则(如单位格式、分组编码合法性等)
    return true;
}

// 批量校验差异记录
DataTable validRecords;
try
{
    validRecords = diffTable.AsEnumerable()
        .Where(row => ValidateRecord(row))
        .CopyToDataTable();
}
catch (InvalidOperationException)
{
    // 无有效记录时创建空表
    validRecords = diffTable.Clone();
}

三、同步差异记录到SQL Server

使用SqlBulkCopy批量插入新增记录,用参数化命令批量更新现有记录,提升同步效率:

void SyncToSqlServer(DataTable validRecords, HashSet<string> sqlStockCodes)
{
    if (validRecords.Rows.Count == 0)
    {
        MessageBox.Show("无有效记录可同步");
        return;
    }

    sqlbagla.Open();
    try
    {
        // 批量插入新增记录
        var insertTable = validRecords.AsEnumerable()
            .Where(row => !sqlStockCodes.Contains(row.Field<string>("PARCAKOD")))
            .CopyToDataTable();

        if (insertTable.Rows.Count > 0)
        {
            using (var bulkCopy = new SqlBulkCopy(sqlbagla))
            {
                bulkCopy.DestinationTableName = "TBLSTSABIT";
                // 映射DB2字段到SQL Server字段(需确保字段名匹配)
                bulkCopy.ColumnMappings.Add("PARCAKOD", "STOK_KODU");
                bulkCopy.ColumnMappings.Add("ADI", "ADI");
                bulkCopy.ColumnMappings.Add("BIRIM", "BIRIM");
                bulkCopy.ColumnMappings.Add("KDVYUZ", "KDVYUZ");
                bulkCopy.ColumnMappings.Add("GRUP1", "GRUP1");
                bulkCopy.WriteToServer(insertTable);
            }
        }

        // 批量更新现有记录
        var updateCmdText = @"UPDATE TBLSTSABIT 
                              SET ADI = @ADI, BIRIM = @BIRIM, KDVYUZ = @KDVYUZ, GRUP1 = @GRUP1
                              WHERE STOK_KODU = @STOK_KODU";

        using (var cmd = new SqlCommand(updateCmdText, sqlbagla))
        {
            cmd.Parameters.Add("@STOK_KODU", SqlDbType.VarChar, 50);
            cmd.Parameters.Add("@ADI", SqlDbType.VarChar, 100);
            cmd.Parameters.Add("@BIRIM", SqlDbType.VarChar, 20);
            cmd.Parameters.Add("@KDVYUZ", SqlDbType.Decimal, 5);
            cmd.Parameters.Add("@GRUP1", SqlDbType.VarChar, 50);

            foreach (DataRow row in validRecords.AsEnumerable().Where(r => sqlStockCodes.Contains(r.Field<string>("PARCAKOD"))))
            {
                cmd.Parameters["@STOK_KODU"].Value = row.Field<string>("PARCAKOD");
                cmd.Parameters["@ADI"].Value = row.Field<string>("ADI");
                cmd.Parameters["@BIRIM"].Value = row.Field<string>("BIRIM");
                cmd.Parameters["@KDVYUZ"].Value = row.Field<decimal>("KDVYUZ");
                cmd.Parameters["@GRUP1"].Value = row.Field<string>("GRUP1");
                cmd.ExecuteNonQuery();
            }
        }

        MessageBox.Show($"成功同步{validRecords.Rows.Count}条记录");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"同步失败:{ex.Message}");
    }
    finally
    {
        sqlbagla.Close();
    }
}

整合后的完整方法

将上述逻辑整合到你的db2StokGetir方法中:

void db2StokGetir()
{
    try
    {
        // 注意:SQL查询需包含所有需对比的字段,仅查编码无法判断字段差异
        DB2DataAdapter db2da = new DB2DataAdapter("SELECT DISTINCT S.PARCAKOD,P.ADI,P.BIRIM,P.KDVYUZ,P.GRUP1 FROM DB2ADMIN.T0020031 S LEFT OUTER JOIN DB2ADMIN.T0050030 P ON S.PARCAKOD = P.KOD WHERE YEAR(S.TARIH) >= 2023 GROUP BY S.PARCAKOD,P.ADI,P.BIRIM,P.KDVYUZ,P.GRUP1", db2bagla);
        SqlDataAdapter sqlda = new SqlDataAdapter("SELECT STOK_KODU, ADI, BIRIM, KDVYUZ, GRUP1 FROM TBLSTSABIT", sqlbagla);

        DataSet db2stok = new DataSet();
        DataSet sqlstok = new DataSet();
        db2da.Fill(db2stok, "DMSSTOK");
        sqlda.Fill(sqlstok, "SQLSTOK");

        // 1. 获取SQL Server库存编码集合
        var sqlStockCodes = new HashSet<string>(sqlstok.Tables[0].AsEnumerable()
            .Select(row => row.Field<string>("STOK_KODU")));

        // 2. 筛选新增和更新的差异记录
        var newRecords = db2stok.Tables[0].AsEnumerable()
            .Where(row => !sqlStockCodes.Contains(row.Field<string>("PARCAKOD")))
            .CopyToDataTable();

        var updateRecords = db2stok.Tables[0].AsEnumerable()
            .Where(row => sqlStockCodes.Contains(row.Field<string>("PARCAKOD")))
            .Join(sqlstok.Tables[0].AsEnumerable(),
                db2Row => db2Row.Field<string>("PARCAKOD"),
                sqlRow => sqlRow.Field<string>("STOK_KODU"),
                (db2Row, sqlRow) => new { DB2Row = db2Row, SQLRow = sqlRow })
            .Where(join => 
                join.DB2Row.Field<string>("ADI") != join.SQLRow.Field<string>("ADI")
                || join.DB2Row.Field<string>("BIRIM") != join.SQLRow.Field<string>("BIRIM")
                || join.DB2Row.Field<decimal>("KDVYUZ") != join.SQLRow.Field<decimal>("KDVYUZ")
                || join.DB2Row.Field<string>("GRUP1") != join.SQLRow.Field<string>("GRUP1"))
            .Select(join => join.DB2Row)
            .CopyToDataTable();

        // 合并差异记录并展示
        DataTable diffTable = new DataTable();
        diffTable.Merge(newRecords);
        diffTable.Merge(updateRecords);
        var diffView = new DataView(diffTable);
        metroGrid1.DataSource = diffView;
        metroLabel1.Text = diffView.Count.ToString();

        // 3. 校验差异记录
        DataTable validRecords;
        try
        {
            validRecords = diffTable.AsEnumerable()
                .Where(row => ValidateRecord(row))
                .CopyToDataTable();
        }
        catch (InvalidOperationException)
        {
            validRecords = diffTable.Clone();
        }

        // 4. 同步到SQL Server(建议提取为独立按钮事件触发)
        // SyncToSqlServer(validRecords, sqlStockCodes);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message.ToString());
    }
}

// 校验方法
bool ValidateRecord(DataRow row)
{
    if (string.IsNullOrEmpty(row.Field<string>("PARCAKOD")) || string.IsNullOrEmpty(row.Field<string>("ADI")))
    {
        MessageBox.Show($"无效记录:库存编码或名称为空,编码[{row.Field<string>("PARCAKOD")}]");
        return false;
    }
    if (row.Field<decimal>("KDVYUZ") < 0 || row.Field<decimal>("KDVYUZ") > 100)
    {
        MessageBox.Show($"无效记录:KDV百分比超出范围,编码[{row.Field<string>("PARCAKOD")}],值[{row.Field<decimal>("KDVYUZ")}]");
        return false;
    }
    return true;
}

// 同步方法
void SyncToSqlServer(DataTable validRecords, HashSet<string> sqlStockCodes)
{
    if (validRecords.Rows.Count == 0)
    {
        MessageBox.Show("无有效记录可同步");
        return;
    }

    sqlbagla.Open();
    try
    {
        var insertTable = validRecords.AsEnumerable()
            .Where(row => !sqlStockCodes.Contains(row.Field<string>("PARCAKOD")))
            .CopyToDataTable();

        if (insertTable.Rows.Count > 0)
        {
            using (var bulkCopy = new SqlBulkCopy(sqlbagla))
            {
                bulkCopy.DestinationTableName = "TBLSTSABIT";
                bulkCopy.ColumnMappings.Add("PARCAKOD", "STOK_KODU");
                bulkCopy.ColumnMappings.Add("ADI", "ADI");
                bulkCopy.ColumnMappings.Add("BIRIM", "BIRIM");
                bulkCopy.ColumnMappings.Add("KDVYUZ", "KDVYUZ");
                bulkCopy.ColumnMappings.Add("GRUP1", "GRUP1");
                bulkCopy.WriteToServer(insertTable);
            }
        }

        var updateCmdText = @"UPDATE TBLSTSABIT 
                              SET ADI = @ADI, BIRIM = @BIRIM, KDVYUZ = @KDVYUZ, GRUP1 = @GRUP1
                              WHERE STOK_KODU = @STOK_KODU";

        using (var cmd = new SqlCommand(updateCmdText, sqlbagla))
        {
            cmd.Parameters.Add("@STOK_KODU", SqlDbType.VarChar, 50);
            cmd.Parameters.Add("@ADI", SqlDbType.VarChar, 100);
            cmd.Parameters.Add("@BIRIM", SqlDbType.VarChar, 20);
            cmd.Parameters.Add("@KDVYUZ", SqlDbType.Decimal, 5);
            cmd.Parameters.Add("@GRUP1", SqlDbType.VarChar, 50);

            foreach (DataRow row in validRecords.AsEnumerable().Where(r => sqlStockCodes.Contains(r.Field<string>("PARCAKOD"))))
            {
                cmd.Parameters["@STOK_KODU"].Value = row.Field<string>("PARCAKOD");
                cmd.Parameters["@ADI"].Value = row.Field<string>("ADI");
                cmd.Parameters["@BIRIM"].Value = row.Field<string>("BIRIM");
                cmd.Parameters["@KDVYUZ"].Value = row.Field<decimal>("KDVYUZ");
                cmd.Parameters["@GRUP1"].Value = row.Field<string>("GRUP1");
                cmd.ExecuteNonQuery();
            }
        }

        MessageBox.Show($"成功同步{validRecords.Rows.Count}条记录");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"同步失败:{ex.Message}");
    }
    finally
    {
        sqlbagla.Close();
    }
}

注意事项

  • 确保SQL Server表TBLSTSABIT的字段名与代码中映射的一致,若字段名不同需调整ColumnMappings和更新语句。
  • 大批量数据同步时,建议添加事务处理,避免部分同步失败导致数据不一致。
  • 可将同步逻辑提取为独立按钮事件,避免在数据加载时直接执行。

内容的提问来源于stack exchange,提问作者Tanju Demir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 17:03:13