我最初建议使用 preserveChanges = true 调用 Merge() 重载,但这仅在 ds2 已经具有指示其与 ds1 差异的行状态时才有效。正如您在问题中所说,这就是您需要完成的。那么,算法?这里有两个:干净、简单、明显的方式;以及对sort-merge join 的改编。没有办法检查 ds2 的每一行,但第二种算法试图通过期望数据是有序的来减少对 ds1 的搜索量。
1) 简单、干净、明显;使用 DataRowCollection.Find(pk) 和 object[].SequenceEqual()。每个表都需要一个主键,但数据不需要排序,主键的类型无关紧要。
for (int i = 0; i < ds2.Tables.Count; i++)
{
foreach (DataRow dr in ds2.Tables[i].Rows)
{
DataRow drOrig = ds1.Tables[i].Rows.Find(dr[0]);
if (drOrig != null)
{
if (!drOrig.ItemArray.SequenceEqual(dr.ItemArray))
{
dr.SetModified();
}
}
else
{
dr.SetAdded();
}
}
}
ds1.Merge(ds2);
2) 更粗、更乱;仍在使用 object[].SequenceEqual()。数据必须是有序的,尽管“pk”/行标识符不必是唯一的。但是,每个单独的表都必须知道其类型,如果类型不同,则不能简单地遍历表。
// Assuming first column of each table is int, primary key; and that all data are ordered by pk.
for (int i = 0; i < ds2.Tables.Count; i++)
{
int indexDs1 = 0
int indexDs2 = 0;
DataRow nextDs1Row = ds1.Tables[i].Rows[indexDs1];
DataRow nextDs2Row = ds2.Tables[i].Rows[indexDs2];
int nextDs1Pk = (int)nextDs1Row[0];
int nextDs2Pk = (int)nextDs2Row[0];
while ((indexDs1 < ds1.Tables[i].Rows.Count) && (indexDs2 < ds2.Tables[i].Rows.Count))
{
if (nextDs1Pk == nextDs2Pk)
{
// Set row state to modified if any differences exist.
if (!nextDs1Row.ItemArray.SequenceEqual(nextDs2Row.ItemArray))
{
nextDs2Row.SetModified();
}
// Advance both iterators by one row.
indexDs1++;
if (indexDs1 < ds1.Tables[i].Rows.Count)
{
nextDs1Row = ds1.Tables[i].Rows[indexDs1];
nextDs1Pk = (int)nextDs1Row[0];
}
indexDs2++;
if (indexDs2 < ds2.Tables[i].Rows.Count)
{
nextDs2Row = ds2.Tables[i].Rows[indexDs2];
nextDs2Pk = (int)nextDs2Row[0];
}
}
else if (nextDs1Pk < nextDs2Pk)
{
// Advance through ds1, doing nothing, until the next pk of ds2 is reached.
do
{
indexDs1++;
if (indexDs1 < ds1.Tables[i].Rows.Count)
{
nextDs1Row = ds1.Tables[i].Rows[indexDs1];
nextDs1Pk = (int)nextDs1Row[0];
}
else
{
break;
}
} while (nextDs1Pk < nextDs2Pk);
}
else //nextDs1Pk > nextDs2Pk
{
// Advance through ds2, setting row state to added, until the next pk of ds1 is reached.
do
{
nextDs2Row.SetAdded();
indexDs2++;
if (indexDs2 < ds2.Tables[i].Rows.Count)
{
nextDs2Row = ds2.Tables[i].Rows[indexDs2];
nextDs2Pk = (int)nextDs2Row[0];
}
else
{
break;
}
} while (nextDs1Pk > nextDs2Pk);
}
}
}
如果您的机器可以很好地执行多任务,并且您不需要对集合中的各个表强制执行外键约束,我会将每个表的行分析设置为单独的任务,并行启动它们,然后合并随着任务的完成,一张一张的表格。如果这足以使算法 1 满足您的要求,我会以简单的名义使用它。它使用的 Find() 和 SequenceEqual() 方法可能是高度优化的,算法 2 在我的测试中并没有执行得更快。如果两者都不够快,并且您对数据有所了解,则可以改进 SequenceEqual()。