OK、A と B の 2 つのエンティティがあるとします。ユーザーごとに、UserId (外部キー)、GroupName、および Name プロパティの一意の組み合わせを持つ 0 個以上の A が存在する場合があります。エンティティ A にも「Value」プロパティがあります。また、ユーザーごとに、UserID (これも外部キー) と "Occurred" プロパティを持つ 0 個以上の B が存在する場合があります。
私の仕事は、7日以上前に発生したプロパティを持つすべてのBを見つけるか、特定のAプロパティの時間数である現在よりも発生したプロパティを持つすべてのBを見つけることです。このコードは完全に機能しているようです:
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 を繰り返しチェックしているという点で、少し複雑に見えます。しかし、私はデータベースの専門家ではありません。私が知っていることは、観測された 1 つの実行が約 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;
これは、Gert が提案したこととほとんど同じです。新しい 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)