web-dev-qa-db-fra.com

Comment obtenir le dernier enregistrement par groupe en SQL

Je suis confronté à un problème plutôt intéressant. J'ai une table avec la structure suivante:

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
    )
)

Le problème est donc que je dois afficher ces données dans une grille. Il y a deux exigences. La première consiste à afficher tous les événements, quelle que soit l'application qui les a lancés. C’est simple: une instruction select fera le travail très facilement.

La deuxième exigence est de pouvoir regrouper les événements par Application. En d’autres termes, affichez tous les événements de manière à ce que, si la variable ApplicationId soit répétée plusieurs fois, ne saisissez que la dernière entrée de chaque application. La clé primaire de l'événement (Id) à ce stade n'est plus nécessaire dans cette requête/vue.

Vous pouvez également remarquer que la date et l'heure de l'événement sont au format chaîne. Cela convient car ils respectent les formats d'heure date standard: mm/jj/aaaa et hh: mm: ss. Je peux tirer ceux-ci comme suit:

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

Mon problème est que si j'utilise les fonctions AGGREGATE sur le reste des colonnes, je ne sais pas comment elles se comporteront:

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

La raison pour laquelle j’ai hésité à le faire est qu’une fonction telle que MAX renverra la plus grande valeur pour une colonne donnée à partir d’un (sous) ensemble d’enregistrements. Il ne faut pas forcément tirer le dernier disque!

Des idées sur la façon de ne sélectionner que le dernier enregistrement application par application?

24
bleepzter

Vous pouvez utiliser une fonction ranking et une expression de table common .

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
42
Anthony Faull

Depuis SQL Server 2012, vous pouvez simplement

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]
7
tartakynov

Après 6 ans, une autre réponse pour 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  
  )  

Bien que j'aime bien la solution Postgresql bien mieux avec sa fonctionnalité distincte qui est plus agréable à taper et beaucoup plus efficace: 

select distinct on (id),val  
from tbl  
order by id,val  
0
Domagoj Peharda

Vous pouvez utiliser une sous-requête avec group by - l'argument group by n'a pas besoin d'être sélectionné. Cela suppose que Id est une incrémentation automatique, de sorte que la plus grande est la plus récente.

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
cordsen

Je pense que cela fonctionnera pour beaucoup de gens désirant aller chercher le dernier enregistrement inséré et il devrait être groupé par:

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

Cela fonctionnera pour ce qui suit:

Structure de la table ID Nom Statut 1 Junaid Oui 2 Jawad Non 3 Fahad Oui 4 Junaid Non 5 Kashif Oui

Résultats après requête ci-dessus ID Nom Statut 4 Junaid Non 2 Jawad Non 3 Fahad Oui 4 Kashif Oui

Il en résulte simplement le dernier enregistrement du groupe par noms.

0
smjunaidiqbal
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)))
0
manji

Parce que vous n'avez pas de clause where dans le sous-ensemble d'enregistrements, ce sont tous les enregistrements. Mais vous mettez max sur la mauvaise colonne, je pense. Cette requête vous donnera ce que vous cherchez.

Select max(applicationid), name, description, CONVERT(DateTime, (Date + ' ' + Time)) 
from event
group by name, description, CONVERT(DateTime, (Date + ' ' + Time)) 
0
Jody

Pour ce faire, vous pouvez utiliser une table de sous-requête ou 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
Jon Egerton