为什么LINQ中的多个位置如此之慢?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么LINQ中的多个位置如此之慢?相关的知识,希望对你有一定的参考价值。

使用C#和Linq to SQL,我发现我使用多个where的查询比使用单个where / and慢几个数量级。

这是查询

using (TeradiodeDataContext dc = new TeradiodeDataContext())
{
    var filterPartNumberID = 71;
    var diodeIDsInBlades = (from bd in dc.BladeDiodes
                            select bd.DiodeID.Value).Distinct();
    var diodesWithTestData = (from t in dc.Tests
                              join tt in dc.TestTypes on t.TestTypeID equals tt.ID
                              where tt.DevicePartNumberID == filterPartNumberID
                              select t.DeviceID.Value).Distinct();
    var result = (from d in dc.Diodes
                  where d.DevicePartNumberID == filterPartNumberID
                  where diodesWithTestData.Contains(d.ID)
                  where !diodeIDsInBlades.Contains(d.ID)
                  orderby d.Name
                  select d);
    var list = result.ToList();
    // ~15 seconds
}

但是,当最终查询中的条件为此时

where d.DevicePartNumberID == filterPartNumberID
& diodesWithTestData.Contains(d.ID)
& !diodeIDsInBlades.Contains(d.ID)
// milliseconds

它非常快。

在调用result之前比较ToList()中的SQL,这里是查询(手动添加值71代替@params)

-- MULTIPLE WHERE
SELECT [t0].[ID], [t0].[Name], [t0].[M2MID], [t0].[DevicePartNumberID], [t0].[Comments], [t0].[Hold]
FROM [dbo].[Diode] AS [t0]
WHERE (NOT (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM (
        SELECT DISTINCT [t2].[value]
        FROM (
            SELECT [t1].[DiodeID] AS [value]
            FROM [dbo].[BladeDiode] AS [t1]
            ) AS [t2]
        ) AS [t3]
    WHERE [t3].[value] = [t0].[ID]
    ))) AND (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM (
        SELECT DISTINCT [t6].[value]
        FROM (
            SELECT [t4].[DeviceID] AS [value], [t5].[DevicePartNumberID]
            FROM [dbo].[Test] AS [t4]
            INNER JOIN [dbo].[TestType] AS [t5] ON [t4].[TestTypeID] = ([t5].[ID])
            ) AS [t6]
        WHERE [t6].[DevicePartNumberID] = (71)
        ) AS [t7]
    WHERE [t7].[value] = [t0].[ID]
    )) AND ([t0].[DevicePartNumberID] = 71)
ORDER BY [t0].[Name]

-- SINGLE WHERE
SELECT [t0].[ID], [t0].[Name], [t0].[M2MID], [t0].[DevicePartNumberID], [t0].[Comments], [t0].[Hold]
FROM [dbo].[Diode] AS [t0]
WHERE ([t0].[DevicePartNumberID] = 71) AND (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM (
        SELECT DISTINCT [t3].[value]
        FROM (
            SELECT [t1].[DeviceID] AS [value], [t2].[DevicePartNumberID]
            FROM [dbo].[Test] AS [t1]
            INNER JOIN [dbo].[TestType] AS [t2] ON [t1].[TestTypeID] = ([t2].[ID])
            ) AS [t3]
        WHERE [t3].[DevicePartNumberID] = (71)
        ) AS [t4]
    WHERE [t4].[value] = [t0].[ID]
    )) AND (NOT (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM (
        SELECT DISTINCT [t6].[value]
        FROM (
            SELECT [t5].[DiodeID] AS [value]
            FROM [dbo].[BladeDiode] AS [t5]
            ) AS [t6]
        ) AS [t7]
    WHERE [t7].[value] = [t0].[ID]
    )))
ORDER BY [t0].[Name]

这两个SQL查询在SSMS中执行<1秒并产生相同的结果。

所以我想知道为什么第一个在LINQ方面较慢。令我担心的是因为我知道我在其他地方使用了多个where,却没有意识到这种严重的性能影响。

This question甚至已经回复了多个&和哪里。而this answer甚至建议使用多个where子句。

任何人都可以解释为什么会发生这种情况吗?

答案

因为写这样的

if (someParam1 != 0)
{
    myQuery = myQuery.Where(q => q.SomeField1 == someParam1)
}
if (someParam2 != 0)
{
    myQuery = myQuery.Where(q => q.SomeField2 == someParam2)
}

与(upd)相同(在someParam1和someParam2!= 0的情况下)

myQuery = from t in Table
          where t.SomeField1 == someParam1
             && t.SomeField2 == someParam2
          select t;

是(未删除)与...相同

myQuery = from t in Table
          where t.SomeField1 == someParam1
          where t.SomeField2 == someParam2
          select t;

UPD

是的,我错了。第二个查询是相同的,首先是不一样的。

第一和第二个查询不完全相同。让我告诉你我的意思。

将lambda表达式写成的第一个查询

t.Where(r => t.SomeField1 == someParam1 && t.SomeField2 == someParam2)

第二个查询为

t.Where(r => r.SomeField1 == someParam1).Where(r => r.SomeField2 == someParam2)

在这种情况下生成的SQL Predicate with SomeField2首先出现(重要的是,见下文)

在第一种情况下,我们得到这个SQL:

SELECT <all field from Table>
  FROM table t
 WHERE t.SomeField1 = :someParam1
   AND t.SomeField2 = :someParam2

在2种情况下,SQL是:

SELECT <all field from Table>
  FROM table t
 WHERE t.SomeField2 = :someParam2
   AND t.SomeField1 = :someParam1

我们看到有两个'相同'的SQL。正如我们所看到的,OP的SQL也是“相同的”,它们在WHERE子句中的谓词顺序是不同的(如我的例子中所示)。我猜SQL优化器生成2个不同的执行计划,可能是(!!!)做NOT EXISTS,然后EXISTS,然后过滤比第一次过滤花费更多的时间,之后做EXISTSNOT EXISTS

UPD2

这是Linq Provider(ORM)的“问题”。我正在使用另一个ORM(linq2db),它在两种情况下都为我生成完全相同的SQL。

以上是关于为什么LINQ中的多个位置如此之慢?的主要内容,如果未能解决你的问题,请参考以下文章

为什么我的递归Fibonacci实现与迭代实现相比如此之慢?

为啥HDFS写入速度如此之慢

为啥HDFS写入速度如此之慢

为啥HDFS写入速度如此之慢

LINQ to XML - 从文件加载 XML 片段

并行LINQ PLinq