0
好吧,假设我有两个实体A和B.对于每个用户,可能有0个或更多个A,具有UserId(外键),GroupName和Name属性的唯一组合。实体A还具有“价值”属性。同样对于每个用户,可能有0个或更多个B,具有用户ID(再次,外键)和“发生”属性。优化LINQ To Entities查询
我的任务是查找所有发生属性超过7天以前的B或具有发生的属性比现在更多 - 特定A属性中的小时数。此代码似乎完美地工作:
DateTime now = DateTime.UtcNow;
DateTime expired = now - TimeSpan.FromDays(7d);
using (DatabaseContext context = DatabaseContext.Create())
{
IQueryable<A> aQ = context.As.Where(a => a.GroupName == "Notifications" && s.Name == "Retention");
IQueryable<B> bQ = context.Bs.Where(
n => aQ.Any(a => a.UserId == b.UserId) ?
b.Occurred < EntityFunctions.AddHours(now, -aQ.FirstOrDefault(a => a.UserId == b.UserId).Value) :
b.Occurred < expired);
IList<B> bs = bQ.ToList();
// ...
}
这就产生沿着这些路线的SQL查询:
SELECT
[Extent1].[ID] AS [ID],
[Extent1].[OCCURRED] AS [OCCURRED],
[Extent1].[USERID] AS [USERID],
FROM [dbo].[B] AS [Extent1]
OUTER APPLY (SELECT TOP (1)
[Extent2].[GROUPNAME] AS [GROUPNAME],
[Extent2].[NAME] AS [NAME],
[Extent2].[USERID] AS [USERID],
[Extent2].[VALUE] AS [VALUE],
FROM [dbo].[A] AS [Extent2]
WHERE (N'Notifications' = [Extent2].[GROUPNAME]) AND (N'Retention' = [Extent2].[NAME]) AND ([Extent2].[USERID] = [Extent1].[USERID])) AS [Element1]
OUTER APPLY (SELECT TOP (1)
[Extent3].[GROUPNAME] AS [GROUPNAME],
[Extent3].[NAME] AS [NAME],
[Extent3].[USERID] AS [USERID],
[Extent3].[VALUE] AS [VALUE],
FROM [dbo].[A] AS [Extent3]
WHERE (N'Notifications' = [Extent3].[GROUPNAME]) AND (N'Retention' = [Extent3].[NAME]) AND ([Extent3].[USERID] = [Extent1].[USERID])) AS [Element2]
WHERE (CASE WHEN (EXISTS (SELECT
1 AS [C1]
FROM [dbo].[A] AS [Extent4]
WHERE (N'Notifications' = [Extent4].[GROUPNAME]) AND (N'Retention' = [Extent4].[NAME]) AND ([Extent4].[USERID] = [Extent1].[USERID])
)) THEN CASE WHEN (CAST([Extent1].[OCCURRED] AS datetime2) < (DATEADD (hours, -([Element1].[VALUE]), @p__linq__0))) THEN cast(1 as bit) WHEN (NOT (CAST([Extent1].[OCCURRED] AS datetime2) < (DATEADD (hours, -([Element2].[VALUE]), @p__linq__0)))) THEN cast(0 as bit) END WHEN ([Extent1].[OCCURRED] < @p__linq__1) THEN cast(1 as bit) WHEN (NOT ([Extent1].[OCCURRED] < @p__linq__1)) THEN cast(0 as bit) END) = 1
请注意,我从实际的东西砍死的代码和SQL查询下来,所以这可能不是完美的代表。但我希望它能够得到重点:这看起来有点毛骨悚然,至少在查询重复检查A以匹配组名,用户名和用户名的方式上。但我不是数据库专家。我所知道的是,观察到的执行时间大约为2.5秒。
有没有更好的方法去做这件事?
感谢您的任何意见!
----解决方案----
感谢Gert为此。
代码的查询弄成这个样子:
var bQ =
from b in context.Bs
let offset = aQ.FirstOrDefault(a => a.UserId == b.UserId)
let expiration = (null != offset) ? EntityFunctions.AddHours(now, -offset.Value) : expired
where b.Occurred < expiration
select b;
这几乎是什么格特建议。新的SQL查询看起来是这样的:
SELECT
[Extent1].[ID] AS [ID],
[Extent1].[OCCURRED] AS [OCCURRED],
[Extent1].[USERID] AS [USERID]
FROM [dbo].[B] AS [Extent1]
OUTER APPLY (SELECT TOP (1)
[Extent2].[GROUPNAME] AS [GROUPNAME],
[Extent2].[NAME] AS [NAME],
[Extent2].[USERID] AS [USERID],
[Extent2].[VALUE] AS [VALUE]
FROM [dbo].[A] AS [Extent2]
WHERE (N'Notifications' = [Extent2].[GROUPNAME]) AND (N'Retention' = [Extent2].[NAME]) AND ([Extent2].[USERID] = [Extent1].[USERID])) AS [Element1]
WHERE CAST([Extent1].[OCCURRED] AS datetime2) < (CASE WHEN ([Element1].[ID] IS NOT NULL) THEN DATEADD (hour, -([Element1].[VALUE]), @p__linq__0) ELSE @p__linq__1 END)
这是MS SQL或MySQL? –
抱歉,抱歉。 SQL Server 2008 R2。 – object88