【问题标题】:Optimizing Linq: IN operator with large data优化 Linq:大数据的 IN 运算符
【发布时间】:2026-01-15 19:20:04
【问题描述】:

如何优化这个查询?

// This will return data ranging from 1 to 500,000 records
List<string> products = GetProductsNames(); 


List<Product> actualProducts = (from p in db.Products
                               where products.Contains(p.Name)
                               select p).ToList();

如果我发送一个包含 44,000 个字符串的列表,则此代码大约需要 30 秒才能填充 actualProducts,但不知道 500,000 条记录需要什么。 :(

有什么方法可以调整这个查询?

注意:每次调用几乎都需要这么多时间(忽略第一次慢速 edmx 调用)

【问题讨论】:

  • GetProductsNames从哪里获取信息?
  • 为什么不在查询中使用联接?
  • @JonSkeet 来自文本文件。
  • 包含 500,000 个产品的文本文件?你为什么不使用数据库?
  • 文本文件中的记录是从另一台服务器导出的,因此我们必须检查本地商店中有多少产品。 (只是为了同步项目)

标签: c# linq optimization


【解决方案1】:

对 500,000 条记录的 IN 查询始终是一种病态的情况。

首先,确保数据库中Name 上有一个索引(可能是非集群的)。

想法(都涉及到 ADO.NET):

  • 使用“表值参数”传递值,INNER JOIN 到 TSQL 中的表值参数
  • 或者,创建一个ProductQuery 形式的表,其中列QueryId(可能是uniqueidentifier)和Name;发明一个 guid 来表示您的查询 (Guid.NewGuid()),然后使用 SqlBulkCopy 将 500,000 对(每行相同的 guid;不同的 guid 是不同的查询)快速推送到表中;然后使用 TSQL 在两个表之间做一个INNER JOIN

实际上,这些非常相似,但第一个可能是第一个尝试的。设置较少。

【讨论】:

  • 尝试后会回复您,是否可以在代码中创建临时文件,而无需更改 edmx?或者有什么方法可以在不修改 edmx 的情况下尝试我们对临时表的建议?
  • @UsmanAzam 据我所知,EF 根本不会使用这些方法。确实如此 - 不需要编辑 edmx,因为编辑 edmx 不会取得任何成果。这是“针对这个特定查询的 ADO.NET/TSQL”的事情,IMO。请记住:EF 只是一个工具;您可以使用不止一种工具。有时,你需要放下锤子,拿出螺丝刀。
  • @UsmanAzam 在您有机会尝试之前,我不会这么急于“接受”它;但是...这肯定是我首先要做的事情
  • 我并不着急。我们的另一个应用程序与您建议的相同。它查找参数,如果它们高于 25,那么它会在 db 中创建一个临时参数,并通过 JOIN 查询执行其余操作。它在 C++ 中使用 SQLAnywhere。我在论坛上问这个问题只是因为是否有更好的解决方案。
  • @UsmanAzam 关于表值参数的要点是它避免了创建临时表的需要;如果你熟悉“表变量”那么:它基本上是这样的,但是作为一个参数
【解决方案2】:

如果您不想使用数据库,可以尝试使用 Dictionary&lt;string,string&gt;

如果没有错,我怀疑products.Contains(p.Name) 很昂贵,因为它是 O(n) 操作。尝试将您的GetProductsNames 返回类型更改为Dictionary&lt;string,string&gt; 或将列表转换为字典

Dictionary<string, string> productsDict = products.ToDictionary(x => x);

所以你手头有一本字典,现在重写查询如下

List<Product> actualProducts = (from p in db.Products
                           where productsDict.ContainsKey(p.Name)
                           select p).ToList();

这将帮助您大大提高性能(缺点是您分配双倍内存优点是性能)。我用非常大的样本进行了测试,结果很好。试试看。

希望这会有所帮助。

【讨论】:

  • 是的,也许只是将所有产品拉到内存中会更快。取决于行数和结构。
  • @usr 我尝试使用 List 进行查询,当切换到 Dictionary
  • @SriramSakthivel 哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇哇。无论如何谢谢:)
【解决方案3】:

您还可以采用散列方法,使用名称列作为传递给散列函数的值;然后您可以迭代 500K 集合,将每个名称置于散列函数中,并测试是否存在于您的本地散列文件中。这将需要比 linq 方法更多的代码,但它可能比重复调用后端执行内部连接要快得多。

【讨论】: