У меня есть таблица, полная действий. Каждое действие выполняется определенным пользователем в определенный срок. Таким образом, он имеет 4 поля: Id, UserId, ActionId и ActionDate.
Сначала я только что сообщил о 10 самых последних действиях, подобных этому:
(from a in db.Action
orderby a.ActionDate descending
select a).Take(10);
Это просто и работает. Но отчет менее полезен, чем я думал. Это потому, что какой-то пользователь может выполнить 10 действий подряд и собрать весь список из 10 лучших. Поэтому я хотел бы сообщить о самых последних действиях, предпринятых для каждого из 10 самых последних активных пользователей.
Из другого вопроса о SO, я получил большую часть пути туда. Похоже, мне нужна функция "группа". Если я сделаю это:
from a in db.Action
orderby a.ActionDate descending
group a by a.UserId into g
select g;
И запустив его в linqpad, я получаю набор результатов IOrderedQueryable<IGrouping<Int32,Action>>
с одной группой для каждого пользователя. Однако он показывает ВСЕ действия, предпринятые каждым пользователем, и набор результатов является иерархическим, и я хотел бы, чтобы он был плоским.
Так что, если моя таблица действий будет выглядеть так
Id UserId ActionId ActionDate
1 1 1 2010/01/09
2 1 63 2010/01/10
3 2 1 2010/01/03
4 2 7 2010/01/06
5 3 11 2010/01/07
Я хочу, чтобы запрос возвращал записи 2, 5 и 4 в указанном порядке. Это показывает мне, для каждого пользователя, самое последнее действие, предпринятое этим пользователем, и все сообщенные действия в порядке, с самым последним наверху. Поэтому я хотел бы видеть:
Id UserId ActionId ActionDate
2 1 63 2010/01/10
5 3 11 2010/01/07
4 2 7 2010/01/06
EDIT:
Мне также трудно выразить это в T-SQL. Этот запрос возвращает мне пользователей и дату их последнего действия:
select
a.UserId,
max(a.ActionDate) as LastAction
from
Action as a
group by
a.UserId
order by
LastAction desc
Но как мне получить доступ к другой информации, которая прикреплена к записи, где было найдено максимальное значение ActionDate?
РЕДАКТИРОВАТЬ 2: Я был рефакторинг и теперь называется Action, но все остальное то же самое. Я принял решение Фрэнка, и оно выглядит следующим образом:
(from u in db.User
join r in db.Read on u.Id equals r.UserId into allRead
where allRead.Count() > 0
let lastRead = allRead.OrderByDescending(r => r.ReadDate).First()
orderby lastRead.ReadDate descending
select new ReadSummary
{
Id = u.Id,
UserId = u.Id,
UserNameFirstLast = u.NameFirstLast,
ProductId = lastRead.ProductId,
ProductName = lastRead.Product.Name,
SegmentCode = lastRead.SegmentCode,
SectionCode = lastRead.SectionCode,
ReadDate = lastRead.ReadDate
}).Take(10);
Это превращается в следующее:
exec sp_executesql N'SELECT TOP (10) [t12].[Id], [t12].[ExternalId], [t12].[FirstName], [t12].[LastName], [t12].[Email], [t12].[DateCreated], [t12].[DateLastModified], [t12].[DateLastLogin], [t12].[value] AS [ProductId], [t12].[value2] AS [ProductName], [t12].[value3] AS [SegmentCode], [t12].[value4] AS [SectionCode], [t12].[value5] AS [ReadDate2]
FROM (
SELECT [t0].[Id], [t0].[ExternalId], [t0].[FirstName], [t0].[LastName], [t0].[Email], [t0].[DateCreated], [t0].[DateLastModified], [t0].[DateLastLogin], (
SELECT [t2].[ProductId]
FROM (
SELECT TOP (1) [t1].[ProductId]
FROM [dbo].[Read] AS [t1]
WHERE [t0].[Id] = [t1].[UserId]
ORDER BY [t1].[ReadDate] DESC
) AS [t2]
) AS [value], (
SELECT [t5].[Name]
FROM (
SELECT TOP (1) [t3].[ProductId]
FROM [dbo].[Read] AS [t3]
WHERE [t0].[Id] = [t3].[UserId]
ORDER BY [t3].[ReadDate] DESC
) AS [t4]
INNER JOIN [dbo].[Product] AS [t5] ON [t5].[Id] = [t4].[ProductId]
) AS [value2], (
SELECT [t7].[SegmentCode]
FROM (
SELECT TOP (1) [t6].[SegmentCode]
FROM [dbo].[Read] AS [t6]
WHERE [t0].[Id] = [t6].[UserId]
ORDER BY [t6].[ReadDate] DESC
) AS [t7]
) AS [value3], (
SELECT [t9].[SectionCode]
FROM (
SELECT TOP (1) [t8].[SectionCode]
FROM [dbo].[Read] AS [t8]
WHERE [t0].[Id] = [t8].[UserId]
ORDER BY [t8].[ReadDate] DESC
) AS [t9]
) AS [value4], (
SELECT [t11].[ReadDate]
FROM (
SELECT TOP (1) [t10].[ReadDate]
FROM [dbo].[Read] AS [t10]
WHERE [t0].[Id] = [t10].[UserId]
ORDER BY [t10].[ReadDate] DESC
) AS [t11]
) AS [value5]
FROM [dbo].[User] AS [t0]
) AS [t12]
WHERE ((
SELECT COUNT(*)
FROM [dbo].[Read] AS [t13]
WHERE [t12].[Id] = [t13].[UserId]
)) > @p0
ORDER BY (
SELECT [t15].[ReadDate]
FROM (
SELECT TOP (1) [t14].[ReadDate]
FROM [dbo].[Read] AS [t14]
WHERE [t12].[Id] = [t14].[UserId]
ORDER BY [t14].[ReadDate] DESC
) AS [t15]
) DESC',N'@p0 int',@p0=0
Если кто-нибудь знает что-то попроще (для спорта), я хотел бы знать, но я думаю, что это, вероятно, достаточно хорошо.