从LINQ到实体查询</int>中排除大型List <int>

时间:2013-12-13 18:21:09

标签: linq linq-to-entities

我有一个包含大量项目的列表 - 最多10,000个。

我正在寻找从IQueryable / List中排除这些内容的最有效方法。

由于获取此ID列表所涉及的过程非常复杂,因此无法在查询中执行此操作。

以下示例的开销非常高,并且想知道是否有人可以解释可能的原因以及是否有更好的方法来实现这一目标?

results = from q1 in results 
  where excludedRecords.All(x => x != q1.ItemId)
  select q1;

2 个答案:

答案 0 :(得分:2)

从查询的形状,我将excludedRecords作为整数列表。此外,由于您将LINQ标记为实体,我将results作为DbSet中的DbContext

这是将本地列表(excludedRecords)与IQueryable组合在一起等待转换为SQL(results)的问题。为了能够将完整表达式(您的查询)转换为SQL,它必须将此本地列表转换为可以作为SQL语句一部分的“某些东西”。使用All()和许多其他基于集合的LINQ语句,并且在加入本地列表时,EF通过从单行表构建临时表(各种类型)来实现此目的。在本地列表中只有5个元素,这看起来像

SELECT ...
    FROM [dbo].[Table] AS [Extent1]
    WHERE  EXISTS (SELECT 
        1 AS [C1]
        FROM  (SELECT 
            1 AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]
        UNION ALL
            SELECT 
            2 AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable2]
        UNION ALL
            SELECT 
            3 AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable3]
        UNION ALL
            SELECT 
            4 AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable4]
        UNION ALL
            SELECT 
            5 AS [C1]
            FROM  ( SELECT 1 AS X ) AS [SingleRowTable5]) AS [UnionAll4]
        WHERE ([Extent1].[Id] = [UnionAll4].[C1]) OR (CASE WHEN ([Extent1].[Id] <> [UnionAll4].[C1]) THEN cast(1 as bit) WHEN ([Extent1].[Id] = [UnionAll4].[C1]) THEN cast(0 as bit) END IS NULL)
    )

虽然这可能会产生巨大的SQL语句,但是当本地列表不包含“太多”元素时(例如,最多1000个),它仍然可行。

允许EF更有效地使用本地列表的唯一声明是ContainsContains可以轻松转换为SQL IN语句。如果我们将您的查询重写为等同于Contains,这也是您问题的答案,...

results = from q1 in results 
          where !excludedRecords.Contains(q1.ItemId)
          select q1;

... SQL查询看起来像

SELECT ...
    FROM [dbo].[Table] AS [Extent1]
    WHERE  NOT ([Extent1].[Id] IN (1, 2, 3, 4, 5))

IN语句可以处理比“临时表”更多的元素,尽管这个数字仍然有限(可能是3000)。

答案 1 :(得分:1)

这只是代码的一个片段,但看起来你有两个列表 - results和excludedRecords。对于结果中的每个元素,您将遍历excludedRecords中的所有元素。这就是为什么它很慢,它是O(N x M)

Linq和sql通过加入来解决这个问题,如果你加入(或等效)你会看到一些不错的表现,因为那样我会像O(NlgM)

看起来像这样(现在无法测试)

var results2 = from q1 in results
                join x in excludedRecords on q1.LeadID = x into joined
                from z in joined.DefaultIfEmpty()
                where z == null
                select q1;
相关问题