SQL-запрос для получения последней строки для каждого экземпляра заданного ключа
Я пытаюсь получить 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.
Ответы
Ответ 1
Попробуйте следующее:
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
Ответ 2
Отличное элегантное решение с функцией окна 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;
Ответ 3
Что-то вроде этого:
select *
from User U1
where time_stamp = (
select max(time_stamp)
from User
where username = U1.username)
должен это сделать.
Ответ 4
Оба вышеупомянутых ответа предполагают, что у вас есть только одна строка для каждого пользователя и time_stamp. В зависимости от приложения и детализации вашего time_stamp это может быть недействительным. Если вам нужно иметь дело со связями time_stamp для данного пользователя, вам нужно будет расширить один из ответов, приведенных выше.
Чтобы записать это в один запрос, потребуется другой вложенный подзапрос - все начнет становиться все более грязным, и производительность может пострадать.
Я бы очень хотел добавить это в качестве комментария, но у меня пока нет репутации 50, так жаль, что вы разместили в качестве нового ответа!
Ответ 5
Не могу оставлять комментарии, но ответ @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;
Ответ 6
Я использую это, потому что я возвращаю результаты из другой таблицы. Хотя я пытаюсь избежать вложенного соединения, если он помогает с меньшим шагом. Ну что ж. Он возвращает то же самое.
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