根据最近的相关实体的值选择实体集合

时间:2012-06-15 21:20:06

标签: c# sql-server entity-framework tsql

我在当前项目的几个地方遇到了同样的困难。

最简单的实例如下:

我有两个相关的实体,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);
    }
}

虽然这可以为我提供我想要的结果,但却非常低效,浪费了大量的资源和执行时间。我要查询的数据库非常大,并且遍历每条记录实际上是不可行的。

使用实体是否有效的方法?我发现很难想象我是唯一一个需要这种功能的人。

2 个答案:

答案 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。