了解 LINQ 如何编译为 ADO.NET SQL 语句
Understanding how LINQ compiles to ADO.NET SQL statements
我有一个看起来非常简单的 LINQ 语句,但它生成的 SQL 对我来说没有意义,而且它的执行时间比我预期的要长得多。我只是想了解 LINQ 在做什么,这样我就可以弄清楚为什么 运行 这么慢。可比较的 SQL 语句用时不到一秒,但 LINQ 大约需要 20 秒。
代码如下:
// 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;
}
在SSMS中,子select returns 3126行没有where
,只有127行有它。 SSMS 中的主 select
returns 365 行没有子 select。完整的 select 和子 select returns 238 行。数据不多。
使用 Visual Studio 的诊断工具,我看到从 LINQ 生成了 14 SQL 条语句。它们中的每一个都是一个简单的 SQL select,我没有看到任何连接,也没有看到 where
比较。这是我在诊断工具事件 window:
中看到的示例 SQL 语句
SELECT
[Extent1].[AlertTargetID] AS [AlertTargetID],
[Extent1].[TargetResourceID] AS [TargetResourceID],
[Extent1].[CreatedAt] AS [CreatedAt],
[Extent1].[CreatedBy] AS [CreatedBy]
FROM [dbo].[AlertTargetResource] AS [Extent1]
还有 13 个类似的 SQL 语句。
这是我要复制的 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')
这是我的问题:
- 为什么 LINQ 生成 14 个简单的 select 而不是一个带有连接和位置的 select?
- 我怎样才能加快我认为是一段简单代码的速度?
在您的方法的第一部分,subQuery
是尚未对数据库进行 运行 的查询。在第二部分(围绕您的 return
语句),您多次调用该查询。
Entity Framework 将如何处理这样的情况并不总是很明显,但这里似乎为 alertEvents
中的每个项目调用了查询。
您真正需要的是由查询 return 编辑的 ID 列表,然后将该列表用于该方法的第二部分。要将查询转换为该查询 return 编辑的数据,您可以使用 ToList()
。此扩展方法将执行查询和 return 数据结果。
在下面的代码中,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;
}
我有一个看起来非常简单的 LINQ 语句,但它生成的 SQL 对我来说没有意义,而且它的执行时间比我预期的要长得多。我只是想了解 LINQ 在做什么,这样我就可以弄清楚为什么 运行 这么慢。可比较的 SQL 语句用时不到一秒,但 LINQ 大约需要 20 秒。
代码如下:
// 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;
}
在SSMS中,子select returns 3126行没有where
,只有127行有它。 SSMS 中的主 select
returns 365 行没有子 select。完整的 select 和子 select returns 238 行。数据不多。
使用 Visual Studio 的诊断工具,我看到从 LINQ 生成了 14 SQL 条语句。它们中的每一个都是一个简单的 SQL select,我没有看到任何连接,也没有看到 where
比较。这是我在诊断工具事件 window:
SELECT
[Extent1].[AlertTargetID] AS [AlertTargetID],
[Extent1].[TargetResourceID] AS [TargetResourceID],
[Extent1].[CreatedAt] AS [CreatedAt],
[Extent1].[CreatedBy] AS [CreatedBy]
FROM [dbo].[AlertTargetResource] AS [Extent1]
还有 13 个类似的 SQL 语句。
这是我要复制的 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')
这是我的问题:
- 为什么 LINQ 生成 14 个简单的 select 而不是一个带有连接和位置的 select?
- 我怎样才能加快我认为是一段简单代码的速度?
在您的方法的第一部分,subQuery
是尚未对数据库进行 运行 的查询。在第二部分(围绕您的 return
语句),您多次调用该查询。
Entity Framework 将如何处理这样的情况并不总是很明显,但这里似乎为 alertEvents
中的每个项目调用了查询。
您真正需要的是由查询 return 编辑的 ID 列表,然后将该列表用于该方法的第二部分。要将查询转换为该查询 return 编辑的数据,您可以使用 ToList()
。此扩展方法将执行查询和 return 数据结果。
在下面的代码中,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;
}