事实证明,这比我想象的要难。基本上,每天系统都会将客户主列表的快照转储到CSV中。它包含约120000条记录和60个字段。约25mb。无论如何,我想报告一个快照和另一个快照之间变化的值。它 不是 计划文件差异,因为它必须与包含客户唯一编号的最左边的列值匹配。可以插入/删除行等。所有字段均为字符串,包括参考编号。
我已经用LINQ编写了一个解决方案,但是它死于更大的数据集。对于10000条记录,这需要17秒。对于120000,需要花费近2个小时来比较这两个文件。现在,它使用出色且免费的’filehelpers’http: //www.filehelpers.com/加载数据,然后仅需几秒钟。但是,检测哪些记录已更改更成问题。以下是2小时的查询:
var changednames = from f in fffiltered from s in sffiltered where f.CustomerRef == s.CustomerRef && f.Customer_Name != s.Customer_Name select new { f, s };
您会推荐哪种方法?我想立即将清单“删节”给那些进行了某种更改的人,然后将更具体的比较应用于该较小的子集。我的一些想法是:
a)使用字典或哈希表-尽管早期测试并未真正显示出改进
b)区分操作-在客户参考字段中使用第一个字符,并且仅与具有相同字符的字符匹配。虽然这可能涉及创建许多单独的集合,但看起来并不优雅。
c)远离类型化的数据安排,并使用数组进行处理。同样,收益不确定。
有什么想法吗?
谢谢!
为了进行下面的讨论,我假设您有一些将CSV文件读入类的方法。我叫那个班MyRecord。
MyRecord
将文件加载到单独的列表中,分别调用NewList和OldList:
NewList
OldList
List<MyRecord> NewList = LoadFile("newFilename"); List<MyRecord> OldList = LoadFile("oldFilename");
使用LINQ可能有一种更优雅的方法,但是想法是进行直接合并。首先,您必须对两个列表进行排序。您的MyRecord类实现了IComparable,或者您提供了自己的比较委托:
IComparable
NewList.Sort(/* delegate here */); OldList.Sort(/* delegate here */);
如果MyRecord实现,则可以跳过委托IComparable。
现在是直接合并。
int ixNew = 0; int ixOld = 0; while (ixNew < NewList.Count && ixOld < OldList.Count) { // Again with the comparison delegate. // I'll assume that MyRecord implements IComparable int cmpRslt = OldList[ixOld].CompareTo(NewList[ixNew]); if (cmpRslt == 0) { // records have the same customer id. // compare for changes. ++ixNew; ++ixOld; } else if (cmpRslt < 0) { // this old record is not in the new file. It's been deleted. ++ixOld; } else { // this new record is not in the old file. It was added. ++ixNew; } } // At this point, one of the lists might still have items. while (ixNew < NewList.Count) { // NewList[ixNew] is an added record ++ixNew; } while (ixOld < OldList.Count) { // OldList[ixOld] is a deleted record }
只有12万条记录,这应该很快执行。如果执行合并只花了从磁盘加载数据的时间,我将感到非常惊讶。
编辑:LINQ解决方案
我思考如何使用LINQ来做到这一点。我无法执行与上述合并完全相同的操作,但是可以在单独的集合中添加,删除和更改项目。 为此,MyRecord必须实现IEquatable<MyRecord>并重写GetHashCode。
IEquatable<MyRecord>
GetHashCode
var AddedItems = NewList.Except(OldList); var RemovedItems = OldList.Except(NewList); var OldListLookup = OldList.ToLookup(t => t.Id); var ItemsInBothLists = from newThing in NewList let oldThing = OldListLookup[newThing.Id].FirstOrDefault() where oldThing != null select new { oldThing = oldThing, newThing = newThing };
在上面,我假设MyRecord具有Id唯一的属性。
Id
如果您只想更改的项目而不是两个列表中的所有项目:
var ChangedItems = from newThing in NewList let oldThing = OldListLookup[newThing.Id].FirstOrDefault() where oldThing != null && CompareItems(oldThing, newThing) != 0 select new { oldThing = oldThing, newThing = newThing };
假设该CompareItems方法将对这两个项目进行深层比较,如果发生了变化则比较为相等或为非零,则返回0。
CompareItems