SQL-запрос для получения самой последней строки для каждого экземпляра данного ключа - PullRequest
64 голосов
/ 17 июля 2009

Я пытаюсь получить ip, user и самую последнюю метку времени из таблицы, которая может содержать как текущий ip для пользователя, так и один или несколько предыдущих ips. Я хотел бы одну строку для каждого пользователя, содержащую самый последний IP-адрес и соответствующую метку времени. Так что если таблица выглядит так:

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

Я ожидаю, что результат запроса будет:

jerry    |  5.6.6.7   |  12
ted      |  8.8.8.8   |  30  

Могу ли я сделать это в одном SQL-запросе? В случае, если это имеет значение, СУБД Postgresql.

Ответы [ 6 ]

93 голосов
/ 17 июля 2009

Попробуйте это:

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
31 голосов
/ 24 декабря 2014

Хорошее элегантное решение с оконной функцией ROW_NUMBER (поддерживается PostgreSQL - см. 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;
8 голосов
/ 17 июля 2009

Примерно так:

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

должен это сделать.

3 голосов
/ 13 августа 2013

Оба приведенных выше ответа предполагают, что у вас есть только одна строка для каждого пользователя и отметка времени. В зависимости от приложения и степени детализации вашей time_stamp это может быть неверным предположением. Если вам нужно разобраться со связями time_stamp для данного пользователя, вам нужно расширить один из ответов, приведенных выше.

Чтобы написать это в одном запросе, потребуется другой вложенный подзапрос - все станет более запутанным и производительность может снизиться.

Я бы хотел добавить это как комментарий, но у меня еще нет 50 репутации, поэтому извините за публикацию в качестве нового ответа!

2 голосов
/ 05 февраля 2015

Пока не могу оставлять комментарии, но ответ @Cristi S радует меня.

В моем сценарии мне нужно было хранить только самые последние 3 записи в Lowest_Offers для всех product_ids.

Нужно переделать свой SQL для удаления - думал, что это будет нормально, но синтаксис неправильный.

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;
0 голосов
/ 05 сентября 2013

Я использовал это, потому что я возвращаю результаты из другой таблицы. Хотя я пытаюсь избежать вложенного соединения, если это поможет без одного шага. Ну что ж. Он возвращает то же самое.

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