简体   繁体   English

了解 LINQ 如何编译为 ADO.NET SQL 语句

[英]Understanding how LINQ compiles to ADO.NET SQL statements

I have what seems like a pretty straightforward LINQ statement, but the SQL it's generating doesn't make sense to me and it's taking much longer to execute than I'm expecting.我有一个看起来非常简单的 LINQ 语句,但它生成的 SQL 对我来说没有意义,而且执行时间比我预期的要长得多。 I'm just trying to understand what LINQ is doing so I can figure out why it's running so slowly.我只是想了解 LINQ 正在做什么,以便我可以弄清楚为什么它运行得如此缓慢。 A comparable SQL statement takes less than a second but the LINQ is taking around 20 seconds.一个类似的 SQL 语句需要不到一秒钟,但 LINQ 需要大约 20 秒。

Here's the code:这是代码:

// This line takes 20 seconds to return.
var alertEvents = GetFilteredAlertEvents(alert.AlertEvents, db).ToList<AlertEvent>();

private static IEnumerable<AlertEvent> GetFilteredAlertEvents(ICollection<AlertEvent> alertEvents, SlalomEREntities db)
{
    Guid marketAlertReceiverGroupId = new Guid(ConfigurationManager.AppSettings["MarketAlertReceiverGroupId"]);
    var subQuery = from ae in alertEvents
                   join tr in db.TargetResources on ae.ResourceId equals tr.ResourceID
                   join atr in db.AlertTargetResources on tr.ResourceID equals atr.TargetResourceID
                   where atr.AlertTargetID == marketAlertReceiverGroupId
                   select ae.AlertEventId;

     return from alertEvent in alertEvents
            where !subQuery.Contains(alertEvent.AlertEventId)
            select alertEvent;
}

In SSMS, the sub-select returns 3126 rows without the where , and only 127 rows with it.在 SSMS 中,子选择返回 3126 行没有where ,只有 127 行有它。 The primary select in SSMS returns 365 rows without the sub-select. SSMS 中的主select返回 365 行而没有子选择。 The full select with sub-select returns 238 rows.带有子选择的完整选择返回 238 行。 Not a lot of data.数据不多。

Using Visual Studio's Diagnostic Tools, I'm seeing 14 SQL statements generated from the LINQ.使用 Visual Studio 的诊断工具,我看到 14 个从 LINQ 生成的 SQL 语句。 Every one of them is a simple SQL select and I don't see any joins nor do I see the where comparison.它们中的每一个都是一个简单的 SQL 选择,我没有看到任何连接,也没有看到where比较。 Here's a sample SQL statement that I'm seeing in the Diagnostic Tools Events window:这是我在“诊断工具事件”窗口中看到的示例 SQL 语句:

SELECT 
[Extent1].[AlertTargetID] AS [AlertTargetID], 
[Extent1].[TargetResourceID] AS [TargetResourceID], 
[Extent1].[CreatedAt] AS [CreatedAt], 
[Extent1].[CreatedBy] AS [CreatedBy]
FROM [dbo].[AlertTargetResource] AS [Extent1]

There are 13 more similar SQL statements.还有 13 条类似的 SQL 语句。

Here's the SQL I'm trying to replicate.这是我试图复制的 SQL。

select *
from AlertEvent ae1
where ae1.AlertEventId not in
(select ae.AlertEventId
from AlertEvent ae 
join TargetResource tr on ae.ResourceId = tr.ResourceID
join AlertTargetResource atr on atr.TargetResourceID = tr.ResourceID
where atr.AlertTargetID = '89bd4ea5-5d56-4b8a-81ba-5a9e5991ba64')

Here are my questions:以下是我的问题:

  1. Why is LINQ genrerating 14 simple selects rather than a single select with joins and the where?为什么 LINQ 对 14 个简单选择进行流派而不是单个选择与连接和 where?
  2. How can I speed up what I thought was a simple bit of code?我怎样才能加快我认为是一段简单代码的速度?

In the first part of your method, subQuery is a query that has not yet been run against the database.在方法的第一部分中, subQuery是尚未针对数据库运行的查询。 In the second part (around your return statement), you are invoking that query a number of times.在第二部分(围绕您的return语句),您多次调用该查询。

It's not always obvious how Entity Framework will handle a case like this, but here it appears to invoke the query for each item in alertEvents . Entity Framework 如何处理这样的情况并不总是很明显,但在这里它似乎为alertEvents每个项目调用查询。

What you really want is the list of IDs returned by the query, and then use that list for the second part of the method.您真正想要的是查询返回的 ID 列表,然后将该列表用于方法的第二部分。 To convert a query to the data returned by that query, you can use ToList() .要将查询转换为该查询返回的数据,您可以使用ToList() This extension method will execute the query and return the data results.此扩展方法将执行查询并返回数据结果。

In the code below, subQuery is now a collection of IDs.在下面的代码中, subQuery现在是 ID 的集合。

private static IEnumerable<AlertEvent> GetFilteredAlertEvents(ICollection<AlertEvent> alertEvents, SlalomEREntities db)
{
    Guid marketAlertReceiverGroupId = new Guid(ConfigurationManager.AppSettings["MarketAlertReceiverGroupId"]);
    var subQuery = (from ae in alertEvents
                    join tr in db.TargetResources on ae.ResourceId equals tr.ResourceID
                    join atr in db.AlertTargetResources on tr.ResourceID equals atr.TargetResourceID
                    where atr.AlertTargetID == marketAlertReceiverGroupId
                    select ae.AlertEventId).ToList();

     return from alertEvent in alertEvents
            where !subQuery.Contains(alertEvent.AlertEventId)
            select alertEvent;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM