Как выбрать самый последний объект этого типа в списке связанных объектов в T-SQL или Linq to Sql? - PullRequest
1 голос
/ 13 января 2010

У меня есть таблица, полная действий. Каждое действие выполняется определенным пользователем в определенный срок. Таким образом, он имеет 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

Если кто-нибудь знает что-то попроще (для спорта), я хотел бы знать, но я думаю, что это, вероятно, достаточно хорошо.

1 Ответ

1 голос
/ 13 января 2010

Возможно, в этом есть некоторые ошибки, но я думаю, что вы хотите объединить коллекцию, а затем с помощью команды «let» выбрать члена этой коллекции:

(
from u in db.Users
join a in db.Actions on u.UserID equals a.UserID into allActions
where allActions.Count() > 0
let firstAction = allActions.OrderByDescending(a => a.ActionDate).First()
orderby firstAction.ActionDate descending
select (u,firstAction)
).Take(10)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...