【问题标题】:Why are multiple where in LINQ so slow?为什么 LINQ 中的多个 where 这么慢?
【发布时间】:2017-12-21 22:17:08
【问题描述】:

使用 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
}

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

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

速度很快。

在调用ToList()之前比较result中的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 中的执行时间

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

This question 甚至回答了多个 & 和 where。 this answer 甚至建议使用多个 where 子句。

谁能解释我的情况为什么会这样?

【问题讨论】:

  • 你需要看一下执行计划。但是,查看 2 个查询,第 2 个查询(单个 where)首先搜索 [t0].[DevicePartNumberID] = 71,然后执行其他条件。第一个查询执行所有其他查询(相关查询),然后查找[t0].[DevicePartNumberID] = 71。这正是我从代码中看到的。有可能我错了,生成的计划与我说的完全不同。
  • 你不会手写这个,所以不要让 ORM 为你做。尽量避免使用连接的嵌套选择(避免 .contains)。这将使生成的sql更简单更快
  • 您的意思是将& 放在第二个条件而不是&& 吗?
  • @GeorgeVovos 请解释您避免使用 .contains 的原因。 ORM 的理念是人们不应该关心 ORM 生成了什么代码:如果事情很慢,那么您可以查看它以查看生成的内容并查看是否可以改进。否则,在应用程序端编写的代码会被开发人员读取,因此代码应该以易于阅读的方式编写(无论 ORM 输出什么内容)。
  • @CodingYoshi ORM 的想法是人们不应该关心 ORM 生成什么代码:这是完全错误的(对于 ORM 和任何其他工具)。我不是在谈论包含是通用的,而是在谈论具体示例。如果 OP 删除了diodeIDsInBlades 和diodesWithTestData 变量,则主查询中的使用连接可能会产生更高效的sql

标签: c# sql-server linq linq-to-sql .net-4.7


【解决方案1】:

因为这样写

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

NOT(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

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

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

第一个使用 lamda 表达式的查询

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

第二次查询

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

在这种情况下,使用 SomeField2 生成的 SQL 谓词首先出现(这很重要,见下文)

在第一种情况下,我们得到这个 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

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

UPD2

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

【讨论】:

  • 你仔细阅读问题了吗? OP 说两个查询产生相同的结果。。所以请解释为什么他们产生相同的结果,但一个比另一个需要更长的时间。
  • 其实不是这样的。第三个myQuery 由编译器完全转换为Table.Where(t =&gt; t.SomeField1 == someParam1).Where(t =&gt; t.SomeField2 == someParam2),并由LINQ to SQL 提供程序转换为WHERE ... AND ...,就像第二个查询一样。
  • 请看我的UPD回答
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-28
  • 2023-04-02
  • 1970-01-01
  • 2012-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多