我在当前项目的几个地方遇到了同样的困难。
最简单的实例如下:
我有两个相关的实体,Request和RequestAction。 每个RequestAction都有一个状态和一个时间戳,并指向一个请求。
我需要根据最近的相关RequestAction的状态查询一组请求。
示例:获取最新RequestAction状态为"打开"。
的所有请求对数据库进行非规范化以使最新状态成为Request实体的属性不是一种选择。
我需要在我的应用程序中的许多其他地方使用相同类型的过滤;我跟踪历史和审计的许多版本的项目,但我通常只想查看每个项目的最新版本。
我可以想到两种解决方案,但两者都没有特别吸引人。
1)我可以手动编写SQL。这个问题的原始SQL答案涉及将表连接到自身,在表的一个实例的时间戳上的外连接比另一个大,然后查找第二个表为空的记录,表明没有更大的时间戳可以被发现。这就是我过去对场景的处理方式。它在数据库中是有效的,因为它使用索引,而不像具有max的子查询。但是,我的开发团队被强烈推动使用实体继续与数据库交谈,因此强烈建议不要使用此解决方案。
2)我可以使用实体将所有值加载到内存中,并在我的代码中手动循环以查找我要查找的值。代码看起来像这样:
var openRequests = new List<Request>();
var allRequests = context.Requests;
foreach (var request in allRequests)
{
var recentAction = request.RequestActions
.OrderByDescending(c => c.ActionTimestamp)
.First();
if (recentAction.Status == "Open")
{
openRequests.Add(request);
}
}
虽然这可以为我提供我想要的结果,但却非常低效,浪费了大量的资源和执行时间。我要查询的数据库非常大,并且遍历每条记录实际上是不可行的。
使用实体是否有效的方法?我发现很难想象我是唯一一个需要这种功能的人。
答案 0 :(得分:2)
SQL方面不会识别任何打开请求操作的最新时间戳:
;WITH MostRecentActions AS
(
SELECT RequestID, ActionTimestamp,
/* other relevant RequestAction columns, */
rn = ROW_NUMBER() OVER (PARTITION BY RequestID ORDER BY ActionTimestamp DESC)
FROM dbo.RequestActions
WHERE Status = 'Open'
)
SELECT RequestID, ActionTimestamp /* , other columns */
FROM MostRecentActions
WHERE rn = 1;
答案 1 :(得分:1)
您可以在单个数据库查询中使用LINQ to Entities进行查询:
var openRequests = context.Requests
.Where(r => r.RequestActions
.OrderByDescending(ra => ra.ActionTimestamp)
.Select(ra => ra.Status)
.FirstOrDefault() == "Open")
.ToList();
生成的SQL如下所示:
SELECT
[Extent1].[Id] AS [Id],
[Extent1].[SomeProp1] AS [SomeProp1],
[Extent1].[SomeProp2] AS [SomeProp2], -- ...etc.
FROM [dbo].[Requests] AS [Extent1]
CROSS APPLY (SELECT TOP (1) [Project1].[Status] AS [Status]
FROM ( SELECT
[Extent2].[Status] AS [Status],
[Extent2].[ActionTimestamp] AS [ActionTimestamp]
FROM [dbo].[RequestActions] AS [Extent2]
WHERE [Extent1].[Id] = [Extent2].[RequestId]
) AS [Project1]
ORDER BY [Project1].[ActionTimestamp] DESC ) AS [Limit1]
WHERE N'Open' = [Limit1].[Status]
我不知道这是否是一个“好”和高性能的SQL。