下面我用c#方法比较两个数据表并返回不匹配记录。
public DataTable GetTableDiff(DataTable dt1, DataTable dt2, string columnName)
{
var StartTime = DateTime.Now;
dt1.PrimaryKey = new DataColumn[] { dt1.Columns["N"] };
dt2.PrimaryKey = new DataColumn[] { dt2.Columns["N"] };
DataTable dtDifference = null;
//Get the difference of two datatables
var dr = from r in dt1.AsEnumerable()
where !dt2.AsEnumerable().Any(r2 => r["N"].ToString().Trim().ToLower() == r2["N"].ToString().Trim().ToLower()
&& r[columnName].ToString().Trim().ToLower() == r2[columnName].ToString().Trim().ToLower())
select r;
if (dr.Any())
{
dtDifference = dr.CopyToDataTable();
}
return dtDifference;
}
此代码有效,但是比较数据表中的10,000条记录需要花费1.24分钟。有什么方法可以使其更快?
N
是主键,columnName
是要比较的列。
谢谢。
答案 0 :(得分:3)
首先,我想问一下您是否曾经在一个简单的for / foreach循环中尝试过此方法并比较了性能?
目前,您正在创建一个新的Enumerable,然后将其复制到数据表中。 如果使用for / foreach循环,则可以在同一迭代中进行比较和复制。
您还应该查看字符串比较。目前,您正在修整,然后转换为小写。由于字符串是不可变的,因此这将为每个字符串的每个操作分配新的内存。因此,在您的where语句中,您基本上每次重复执行此操作(最多)8次。
我还要问您是否真的需要Trim()
?一个DT可能在字符串的前面有一个空格,而另一个没有吗?还是比较仍然是正确的?除非确实需要,否则不要修剪字符串。
然后,您应该使用不区分大小写的字符串比较,而不要转换ToLower
。这样会更快。据MS称StringComparison.OrdinalIgnoreCase
表现更好。
做这些,然后比较效果,看看有什么不同
另请参阅: https://docs.microsoft.com/en-us/dotnet/standard/base-types/best-practices-strings
更新:
这引起了我的兴趣,所以我回去做了一些测试。 我在两个数据表中生成了10,000行随机(ish)数据,每两个数据表都会匹配,并执行您的比较与简化的循环比较(使用String比较):
for (int i = 0; i < dt1.Rows.Count; i++)
{
if (dt1.Rows[i]["N"].ToString().Equals(dt2.Rows[i]["N"].ToString(), StringComparison.OrdinalIgnoreCase)
&& dt1.Rows[i][columnName].ToString().Equals(dt2.Rows[i][columnName].ToString(), StringComparison.OrdinalIgnoreCase))
{
dtDifference.Rows.Add(dt1.Rows[i].ItemArray);
}
}
您的代码= 66,000ms-> 75,000ms
对于循环代码= 12毫秒-> 20毫秒
差异很大!
然后,我使用for循环方法进行了比较,但对字符串使用了两种不同的字符串比较类型。使用我的字符串比较,对比您的。但是我为此必须测试一百万行,以取得显着差异。
此差异介于200毫秒至800毫秒之间
因此在这种情况下,字符串比较似乎不是主要因素。
因此,看来创建数据行的Linq查询占用了大多数时间,而不是比较行本身。
因此,切换到使用for循环,一切都会再次好起来!
答案 1 :(得分:1)
如果修剪了string
中的DataTable
值,则可以使用DataRowCollection.Find
方法基于主键结合DataTable.CaseSensitive
属性进行快速查找。但是由于并非如此,因此您不能使用ADO.NET的内置功能。幸运的是,使用ToLookup
LINQ方法可以轻松完成同一件事,该方法返回一个只读的一对多词典。
public DataTable GetTableDiff(DataTable dt1, DataTable dt2, string columnName)
{
IEqualityComparer<string> comparer = StringComparer.OrdinalIgnoreCase;
var lookup = dt2.AsEnumerable().ToLookup(row => row["N"].ToString().Trim(),
comparer);
var diffList = dt1.AsEnumerable()
.Where(r1 => !lookup[r1["N"].ToString().Trim()].Any(r2 => comparer.Equals(
r1[columnName].ToString().Trim(), r2[columnName].ToString().Trim())))
.ToList();
if (diffList.Count == 0) return null;
return diffList.CopyToDataTable();
}