web-dev-qa-db-fra.com

Requête SQL pour obtenir la ligne la plus récente pour chaque instance d'une clé donnée

J'essaie d'obtenir l'IP, l'utilisateur et l'horodatage le plus récent d'une table pouvant contenir à la fois l'adresse IP actuelle d'un utilisateur et un ou plusieurs IPS précédents. Je voudrais une ligne pour chaque utilisateur contenant l'adresse IP la plus récente et l'horodatage associé. Donc si une table ressemble à ceci:

username      |  ip      |  time_stamp  
--------------|----------|--------------  
ted           | 1.2.3.4  | 10  
jerry         | 5.6.6.7  | 12  
ted           | 8.8.8.8  | 30  

Je m'attendrais à ce que le résultat de la requête soit:

jerry    |  5.6.6.7   |  12
ted      |  8.8.8.8   |  30  

Puis-je le faire dans une seule requête SQL? Si cela compte, le SGBD est Postgresql.

63
alanc10n

Essaye ça:

Select u.[username]
      ,u.[ip]
      ,q.[time_stamp]
From [users] As u
Inner Join (
    Select [username]
          ,max(time_stamp) as [time_stamp]
    From [users]
    Group By [username]) As [q]
On u.username = q.username
And u.time_stamp = q.time_stamp
93
Chris Nielsen

Belle solution élégante avec la fonction de fenêtre ROW_NUMBER (supporté par PostgreSQL - voir dans SQL Fiddle):

SELECT username, ip, time_stamp FROM (
 SELECT username, ip, time_stamp, 
  ROW_NUMBER() OVER (PARTITION BY username ORDER BY time_stamp DESC) rn
 FROM Users
) tmp WHERE rn = 1;
30
Cristi S.

Quelque chose comme ça:

select * 
from User U1
where time_stamp = (
  select max(time_stamp) 
  from User 
  where username = U1.username)

devrait le faire.

7
Kim Gräsman

Les deux réponses ci-dessus supposent que vous n'avez qu'une seule ligne pour chaque utilisateur et time_stamp. Selon l’application et la granularité de votre horodatage, cette hypothèse peut ne pas être valide. Si vous avez besoin de traiter les liens de time_stamp pour un utilisateur donné, vous devez approfondir l’une des réponses données ci-dessus.

L'écrire dans une requête nécessiterait une autre sous-requête imbriquée - les choses commenceraient à devenir plus compliquées et les performances pourraient en souffrir.

J'aurais bien aimé ajouter ceci en tant que commentaire, mais je n'ai pas encore 50 ans, je suis désolé de vous avoir envoyé une nouvelle réponse!

3
user2323503

Vous ne pouvez pas encore publier de commentaires, mais la réponse de @Cristi S est un régal pour moi. 

Dans mon scénario, je devais conserver uniquement les 3 enregistrements les plus récents dans Lowest_Offers pour tous les product_ids.

Nécessité de retravailler son code SQL pour le supprimer - pensait que cela irait, mais la syntaxe est fausse.

DELETE from (
SELECT product_id, id, date_checked,
  ROW_NUMBER() OVER (PARTITION BY product_id ORDER BY date_checked DESC) rn
FROM lowest_offers
) tmp WHERE > 3;
2
err1

Je l'utilise parce que je renvoie les résultats d'une autre table. Bien que j'essaie d'éviter la jointure imbriquée si cela aide avec un pas de moins. Tant pis. Il retourne la même chose.

select
users.userid
, lastIP.IP
, lastIP.maxdate

from users

inner join (
    select userid, IP, datetime
    from IPAddresses
    inner join (
        select userid, max(datetime) as maxdate
        from IPAddresses
        group by userid
        ) maxIP on IPAddresses.datetime = maxIP.maxdate and IPAddresses.userid = maxIP.userid
    ) as lastIP on users.userid = lastIP.userid
0
jawz101