Как получить последнюю запись на группу в SQL - PullRequest
24 голосов
/ 01 июня 2011

Я столкнулся с довольно интересной проблемой.У меня есть таблица со следующей структурой:

CREATE TABLE [dbo].[Event]
(
    Id int IDENTITY(1,1) NOT NULL,
    ApplicationId nvarchar(32) NOT NULL,
    Name nvarchar(128) NOT NULL,
    Description nvarchar(256) NULL,
    Date nvarchar(16) NOT NULL,
    Time nvarchar(16) NOT NULL,
    EventType nvarchar(16) NOT NULL,
    CONSTRAINT Event_PK PRIMARY KEY CLUSTERED ( Id ) WITH (
        PAD_INDEX = OFF, 
        STATISTICS_NORECOMPUTE = OFF, 
        IGNORE_DUP_KEY = OFF, 
        ALLOW_ROW_LOCKS = ON, 
        ALLOW_PAGE_LOCKS  = ON
    )
)

Так что проблема в том, что я должен отображать эти данные в сетке.Есть два требования.Первый - отображать все события независимо от того, какое приложение их бросило.Это просто - оператор выбора сделает работу очень легко.

Второе требование - возможность группировать события по Application.Другими словами, отображать все события таким образом, что если ApplicationId повторяется более одного раза, захватить только последнюю запись для каждого приложения.Первичный ключ события (Id) в этой точке больше не требуется в этом запросе / представлении.

Вы также можете заметить, что дата и время события представлены в строковом формате.Это нормально, потому что они следуют стандартным форматам даты и времени: мм / дд / гггг и чч: мм: сс.Я могу вытащить их следующим образом:

Convert( DateTime, (Date + ' ' +  Time)) AS 'TimeStamp'

Моя проблема в том, что если я использую функции AGGREGATE для остальных столбцов, я не знаю, как они будут себя вести:

SELECT
    ApplicationId,
    MAX(Name),
    MAX(Description),
    MAX( CONVERT(DateTime, (Date + ' ' + Time))) AS 'TimeStamp',
    MAX( EventType )
FROM
    Event
GROUP BY
    ApplicationId

Причина, по которой я не решаюсь это сделать, заключается в том, что такая функция, как MAX, будет возвращать наибольшее значение для данного столбца из (под) набора записей.Нет необходимости тянуть последнюю запись!

Есть идеи, как выбрать только последнюю запись для каждого приложения?

Ответы [ 8 ]

42 голосов
/ 01 июня 2011

Вы можете использовать функцию ранжирования и общее табличное выражение .

WITH e AS
(
     SELECT *,
         ROW_NUMBER() OVER
         (
             PARTITION BY ApplicationId
             ORDER BY CONVERT(datetime, [Date], 101) DESC, [Time] DESC
         ) AS Recency
     FROM [Event]
)
SELECT *
FROM e
WHERE Recency = 1
7 голосов
/ 18 февраля 2014

С SQL Server 2012 вы можете просто

SELECT 
    [Month]
    , [First] = FIRST_VALUE(SUM([Clicks])) OVER (ORDER BY [Month])
    , [Last]  = FIRST_VALUE(SUM([Clicks])) OVER (ORDER BY [Month] DESC)
FROM 
    [dbo].[Table]
GROUP BY [Month]
ORDER BY [Month]
0 голосов
/ 25 июля 2017

Через 6 лет другой ответ для SQL Server:

select t1.[Id], t2.[Value]  
from [dbo].[Table] t1  
  outer apply (  
    select top 1 [Value]  
      from [dbo].[Table] t2  
        where t2.[Month]=t1.[Month]  
      order by [dbo].[Date] desc  
  )  

Хотя мне больше нравится решение Postgresql с его отличительной функциональностью, которая приятнее печатать и намного эффективнее:

select distinct on (id),val  
from tbl  
order by id,val  
0 голосов
/ 07 мая 2013

Я думаю, что это будет работать для многих, желающих получить последнюю вставленную запись, и она должна быть сгруппирована по:

select * from (select * from TableName ORDER BY id DESC) AS x GROUP BY FieldName

Это будет работать для следующего:

Структура таблицы Идентификатор Имя Статус 1 Джунаид Да 2 Джавад Нет 3 Фахад Да 4 Джунаид Нет 5 Кашиф Да

Результаты после запроса выше Идентификатор Имени Статус 4 Джунаид Нет 2 Джавад Нет 3 Фахад Да 4 Кашиф Да

Это просто результат последней записи группы по именам.

0 голосов
/ 01 июня 2011

Вы можете использовать подзапрос с group by - группа по аргументу не обязательно должна быть в select.Предполагается, что Id - автоинкремент, поэтому самый большой - самый последний.

SELECT
    ApplicationId,
    Name,
    Description,
    CONVERT(DateTime, (Date + ' ' + Time)) AS 'TimeStamp',
    EventType
FROM
    Event e
WHERE
    Id in (select max(Id) from Event GROUP BY ApplicationId)
0 голосов
/ 01 июня 2011

Для этого можно использовать таблицу подзапросов или CTE:

;WITH CTE_LatestEvents as (
SELECT
    ApplicationId,    
    MAX( CONVERT(DateTime, (Date + ' ' + Time))) AS 'LatestTimeStamp',
FROM
    Event
GROUP BY
    ApplicationId
)
SELECT
    ApplicationId,
    Name,
    Description,
    CONVERT(DateTime, (Date + ' ' + Time))) AS 'TimeStamp',
    EventType
FROM
    Event e
    Join CTE_LatestEvents le 
        on e.applicationid = le.applicationid
        and CONVERT(DateTime, (e.Date + ' ' + e.Time))) = le.LatestTimeStamp
0 голосов
/ 01 июня 2011

Поскольку у вас нет предложения where, подмножество записей - это все записи. Но я полагаю, что вы ставите Макс не на те столбцы. Этот запрос даст вам то, что вы ищете.

Select max(applicationid), name, description, CONVERT(DateTime, (Date + ' ' + Time)) 
from event
group by name, description, CONVERT(DateTime, (Date + ' ' + Time)) 
0 голосов
/ 01 июня 2011
SELECT
    E.ApplicationId,
    E.Name,
    E.Description,
    CONVERT(DateTime, (E.Date + ' ' + E.Time)) AS 'TimeStamp',
    E.EventType
FROM
    Event E
    JOIN (SELECT ApplicationId,
                 MAX(CONVERT(DateTime, (Date + ' ' + Time))) AS max_date
            FROM Event
        GROUP BY ApplicationId) EM 
      on EM.ApplicationId = E.ApplicationId
     and EM.max_date = CONVERT(DateTime, (E.Date + ' ' + E.Time)))
...