C# 如何在csv导入列表上加速linq查询

C# 如何在csv导入列表上加速linq查询,c#,linq,csv,large-data,C#,Linq,Csv,Large Data,我的任务是将170万条记录与csv文件中传递给我的一些结果进行匹配 下面代码的一点背景,我有两个列表 证书包含5个属性,ID相当于PK 包含证书列表中应包含的ID列表的订单 我需要将两者匹配起来,并对找到的证书对象执行一些操作 foreach (Classes.CertOrder.IDS OrderUnitID in Order.AllIDs) { var Cert = (from C in Certs where

我的任务是将170万条记录与csv文件中传递给我的一些结果进行匹配

下面代码的一点背景,我有两个列表

证书包含5个属性,ID相当于PK

包含证书列表中应包含的ID列表的订单

我需要将两者匹配起来,并对找到的证书对象执行一些操作

            foreach (Classes.CertOrder.IDS OrderUnitID in Order.AllIDs)
            {
                var Cert = (from C in Certs where C.ID.ToUpper() == OrderUnitID.ID.ToUpper() select C).FirstOrDefault();
                if (Cert != null)
                {
                    Output.add(Cert)
                    OrderUnitID.fulfilled = true;
                }

            }
这段代码可以工作,但它的速度非常慢(我猜这是由于记录的数量),有什么方法可以加快速度吗

编辑到添加,希望能够将数据添加到SQL server以运行查询,但是数据不允许离开正在处理文件的工作站,甚至不允许以未加密的形式接触磁盘


结合下面的有用答案,我已将输出更改为基于列表,按ID对两个列表进行预排序,现在处理需要几秒钟而不是几小时!谢谢堆栈溢出

为什么数据库查找更快? 原因之一是索引

您可以使用创建内存中列表的索引


然后对每个循环使用并行程序来加快速度。

证书创建字典:

var certsMapping = Certs
    .ToDictionary(_ => _.ID.ToUpper());

foreach (Classes.CertOrder.IDS OrderUnitID in Order.AllIDs)
{
    if (certMapping.TryGetValue(OrderUnitID.ID.ToUpper(), out var cert))
    {
        Output.add(cert);
        OrderUnitID.fulfilled = true;
    }
}

在公认答案的基础上再扩展一些其他选项。OrdinalingOrecase提供了最佳的单线程性能,而并行化则提供了最佳的总体性能

class Item { public string Id { get; set; } }

class Program
{
    private static Random rng = new Random();
    private static string characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    static void Main(string[] args)
    {
        var list = Enumerable.Range(1, 2_700_000)
            .Select(x => string.Join("", Enumerable.Range(5, rng.Next(20)).Select(y => characters[rng.Next(0, characters.Length)])))
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .Select(x => new {Order = rng.Next(), Item = new Item {Id = x }})
            .OrderBy(x => x.Order)
            .Select(x => x.Item)
            .ToList();

        Console.WriteLine("Master List Size: {0}", list.Count);

        var matches = list.Take(350_000).Select(x => x.Id).ToList();

        Console.WriteLine("Matches List Size: {0}", matches.Count);

        var dict = list.ToDictionary(x => x.Id, x => x, StringComparer.CurrentCultureIgnoreCase);

        var results = new List<Item>();

        var sw = new Stopwatch();

        Console.WriteLine("CurrentCultureIgnoreCase Elapsed Time (avg): {0}",
            Enumerable.Range(1, 10).Select(x =>
            {
                sw.Start();

                foreach (var m in matches)
                    if (dict.TryGetValue(m, out var item))
                        results.Add(item);

                sw.Stop();

                var t = sw.ElapsedMilliseconds;

                sw.Reset();

                return t;
            }).Average());


        dict = list.ToDictionary(x => x.Id.ToUpper(), x => x);

        Console.WriteLine("ToUpper() Elapsed Time (avg): {0}",
            Enumerable.Range(1, 10).Select(x =>
            {
                sw.Start();

                foreach (var m in matches)
                    if (dict.TryGetValue(m.ToUpper(), out var item))
                        results.Add(item);

                sw.Stop();

                var t = sw.ElapsedMilliseconds;

                sw.Reset();

                return t;
            }).Average());


        dict = list.ToDictionary(x => x.Id, x => x, StringComparer.OrdinalIgnoreCase);

        Console.WriteLine("OrdinalIgnoreCase Elapsed Time (avg): {0}",
            Enumerable.Range(1, 10).Select(x =>
            {
                sw.Start();

                foreach (var m in matches)
                    if (dict.TryGetValue(m, out var item))
                        results.Add(item);

                sw.Stop();

                var t = sw.ElapsedMilliseconds;

                sw.Reset();

                return t;
            }).Average());
    }
}

var cDict = new ConcurrentDictionary<string,Item>(dict);
var cResults = new ConcurrentBag<Item>();

Console.WriteLine("Parallel Elapsed Time (avg): {0}",
    Enumerable.Range(1, 10).Select(x =>
    {
        sw.Start();

        Parallel.ForEach(matches, new ParallelOptions{MaxDegreeOfParallelism = 20}, m =>
        {
            if (cDict.TryGetValue(m, out var item))
                cResults.Add(item);
        });
        sw.Stop();

        var t = sw.ElapsedMilliseconds;

        sw.Reset();

        return t;
    }).Average());

有什么办法可以加快速度吗是的,很多。哪一个最好,你得做些研究。查看
Task.Run()
可能是一个选项,或者
parrelell.ForEach
,这会“更快”吗?也许,您必须测试它。当然,如果您将这个CSV加载到SQL之类的东西中,它可以更快地处理这些数据
Dictionary
可能会加快查找速度,但您必须将其与最初创建Dictionary的开销进行权衡。基本上,这太广泛了。如果您不希望有太多匹配项,那么请缓存所有证书id,对id进行搜索,如果您有matach,则进行查询以按id提取证书对象。证书中应该只有一个匹配项。AllID但是证书列表包含多个订单的结果。
证书中是否存在重复项?我的意思是,具有相同
ID
的项目?两个ID列表中都没有重复项。谢谢丹尼斯,字典确实帮了大忙,我现在每分钟处理数千个而不是数百个。要翻出170万条记录还需要一段时间,但至少现在要在我退休前完成。哪个列表有170万条记录?请张贴两个列表大小。证书包含1.7mil,所有订单ID包含90k到350k ID,具体取决于正在处理的订单对象。从证书列表中删除证书的附加处理是否值得花费时间,例如,当证书匹配并处理后,将其从列表中删除,因为它不会被使用或再次需要。Hmm。字典查找工作非常快。不需要从字典中删除项-它不会执行得更快。什么是
输出
?设置
OrderUnitID.Completed
是否只是设置一个
bool
(无副作用)?我的意思是,只要遍历
列表
(如果
Order.AllIDs
是一个列表)和字典查找应该不会花费太多时间。抱歉,output.add只是一个示例行,实际行包含安全敏感信息。它基本上是将cert对象的一些属性和一个masterkey添加到一个字符串中,该字符串稍后将输出到csv。Order.AllIDs是IDS对象的列表,其中包含ID作为字符串和布尔值。
 Master List Size: 2158882
 Matches List Size: 350000
 CurrentCultureIgnoreCase Elapsed Time (avg): 298.2
 ToUpper() Elapsed Time (avg): 179.6
 OrdinalIgnoreCase Elapsed Time (avg): 163.9
 Parallel Elapsed Time (avg): 74.6