2012-11-09 182 views
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) 
+0

這是MS SQL或MySQL? –

+0

抱歉,抱歉。 SQL Server 2008 R2。 – object88

回答

2

我認爲這將查詢A表只有一次:

from b in context.Bs 
    let offset = aQ.FirstOrDefault(a => a.UserId == b.UserId).Value 
    let dd = offset.HasValue 
     ? EntityFunctions.AddHours(now, -offset) 
     : expired 
where b.Occurred < dd