як я запитую sql для останньої дати запису для кожного користувача


228

У мене є таблиця, яка представляє записи колекції щодо того, коли користувач увійшов у систему.

username, date,      value
--------------------------
brad,     1/2/2010,  1.1
fred,     1/3/2010,  1.0
bob,      8/4/2009,  1.5
brad,     2/2/2010,  1.2
fred,     12/2/2009, 1.3

etc..

Як створити запит, який дасть мені останню дату для кожного користувача?

Оновлення: я забув, що мені потрібно мати значення, яке відповідає останнім датам.


7
Яку базу даних ви використовуєте? MySQL, SQL-сервер, Oracle, ...?
Пітер Ланг

1
Вам потрібне значення, яке відповідає останній даті, або максимальне значення ТА максимальна дата?
Меттью Джонс

Відповіді:


381
select t.username, t.date, t.value
from MyTable t
inner join (
    select username, max(date) as MaxDate
    from MyTable
    group by username
) tm on t.username = tm.username and t.date = tm.MaxDate

3
Чи працюватиме з postgresql ця версія швидше, ніж використання IN (підзапит) замість внутрішнього з'єднання?
TheOne

3
@TheOne, як мій досвід, використовувати внутрішнє з'єднання швидше, ніж у стані
dada

14
Обережно з таким підходом: він може повертати більше одного рядка на користувача, якщо у них більше одного запису на дату ( max(date)повертає дату, яка приєднається до кількох записів). Щоб уникнути цього питання, краще використовувати рішення @ dotjoe: stackoverflow.com/a/2411763/4406793 .
Марко Рой

@RedFilter Це спрацювало ідеально для моєї проблеми. Дякую за такий технічний запит. До речі, я використовував дату замість дати, щоб уникнути отримання декількох результатів для певної дати
Мухаммед Хан

навіщо вам потрібне, щоб угрупування 'і t.date = tm.MaxDate' було недостатньо?
Дулді,

125

Використання віконних функцій (працює в Oracle, Postgres 8.4, SQL Server 2005, DB2, Sybase, Firebird 3.0, MariaDB 10.3)

select * from (
    select
        username,
        date,
        value,
        row_number() over(partition by username order by date desc) as rn
    from
        yourtable
) t
where t.rn = 1

1
Варто уточнити, який продукт / версія Sybase. Він не працює на Sybase ASE 16.
levant

2
Велика перевага цього підходу полягає в тому, що він гарантовано завжди повертати лише один рядок за розділом ( usernameв даному випадку) і навіть не вимагає унікального "упорядкованого" поля (наприклад, приєднання max(date)до інших відповідей).
Marco Roy

1
Просто додайте щось до того, що сказав @MarcoRoy, якщо у вас більше одного запису з однаковою максимальною датою, якщо ви змінюєте запит, як, наприклад, під час його налагодження, інший запис може отримати номер рядка 1, так результати можуть бути непослідовними. Але поки вам насправді все одно, це не повинно бути проблемою. Це можна вирішити, якщо ви додасте ПК після дати. Наприклад: order by date desc, id desc).
Андрій

40

Я бачу, що більшість розробників використовують вбудований запит, не враховуючи його вплив на величезні дані.

Просто ви можете досягти цього:

SELECT a.username, a.date, a.value
FROM myTable a
LEFT OUTER JOIN myTable b
ON a.username = b.username 
AND a.date < b.date
WHERE b.username IS NULL
ORDER BY a.date desc;

3
насправді це працює лише для дублікатів, якщо у вас більше двох значень, умова a.date <b.date не працює, це означає, що це не є загальним рішенням, хоча важлива ідея роботи з НАЗАЄМНІСТЬМИ ЛІФУ. річ у цій відповіді.
iversoncru

Цікаво, що Sybase ASE 16 чудово працює для менших (<10k рядків) таблиць, але при більших (> 100k рядок) він висить ... Я думав, що це буде ідеальний приклад, що реляційні БД повинні відрізнятися від ...
levant pied

1
@levantpied ... Так, більша кількість наборів даних залишається дорогою. Ви можете налаштувати продуктивність, поставивши умову фільтра на себе приєднатись, щоб якось обробити її.
sujeet

21

Щоб отримати весь рядок, що містить максимальну дату для користувача:

select username, date, value
from tablename where (username, date) in (
    select username, max(date) as date
    from tablename
    group by username
)

1
Робота для MySQL
School Boy

1
Будьте уважні, що це дасть вам дублікати, якщо для певного користувача є більше одного запису з тією ж датою. Ви можете або не хочете цього.
Андрій

Цей sql повільний в Oracle і в пункті, він не буде використовувати індекс
meadlai

9
SELECT *     
FROM MyTable T1    
WHERE date = (
   SELECT max(date)
   FROM MyTable T2
   WHERE T1.username=T2.username
)

4
Хоча це ще одне можливе рішення, це, як правило, не гарний спосіб вирішити це. Виконання цього способу призведе до того, що внутрішній запит буде запускатися один раз для кожного імені таблиці, спричиняючи велике сповільнення будь-якої таблиці значного розміру. Здійснення окремого запиту, який не має елемента з першого запиту в пункті де, а потім поєднання двох таблиць, як правило, буде швидшим.
Скотт Чемберлен

Це є приємною особливістю того, що це одне з більш зрозумілих рішень, яке не стосується конкретної реалізації.
Михайло Щепаняк

7

З мого досвіду, найшвидший спосіб - це взяти кожен рядок, для якого в таблиці немає нових рядків.

Ще одна перевага полягає в тому, що використовуваний синтаксис дуже простий і що значення запиту зрозуміти досить просто (візьміть усі рядки таким чином, щоб не існувало новішого рядка для імені користувача, що розглядається).

НЕ Є

SELECT username, value
FROM t
WHERE NOT EXISTS (
  SELECT *
  FROM t AS witness
  WHERE witness.username = t.username AND witness.date > t.date
);

ROW_NUMBER

SELECT username, value
FROM (
  SELECT username, value, row_number() OVER (PARTITION BY username ORDER BY date DESC) AS rn
  FROM t
) t2
WHERE rn = 1

ВНУТРІШНЄ З'ЄДНАННЯ

SELECT t.username, t.value
FROM t
INNER JOIN (
  SELECT username, MAX(date) AS date
  FROM t
  GROUP BY username
) tm ON t.username = tm.username AND t.date = tm.date;

ЛІВНІЙ ЗОВНІШНІ ПРИЄДНАЙТЕ

SELECT username, value
FROM t
LEFT OUTER JOIN t AS w ON t.username = w.username AND t.date < w.date
WHERE w.username IS NULL

У мене виникають труднощі з розумінням версії НЕ ІСНУЄТЬСЯ. Ви не пропустили агрегацію в частині підзапиту? Якщо я запускаю це на своєму столі, я отримую лише 3 записи співробітників від 40 працівників, які я маю в таблиці. Я повинен отримати щонайменше 40 записів. Чи не слід у внутрішньому запиті співставляти імені користувача?
Нарше

Це працює для мене, використовуючи наступне:SELECT username, value FROM t WHERE NOT EXISTS ( SELECT * FROM t AS witness WHERE witness.date > t.date AND witness.username = t.username );
Нарше,

Я переглянув НЕ ІСНУЄ і, схоже, поверне лише вищий запис для всіх користувачів, а не: "запит, який дасть мені останню дату для кожного користувача".
Tasos Zervos

Ви дійсно праві, я оновлюю свій запит. Дякуємо за ваше зауваження! @Narshe Вибачте, я пропустив ваші коментарі чомусь: / Але ви абсолютно праві.
Фабіан Піцке

2

Це має дати вам правильний результат для вашого відредагованого питання.

У підзапиті обов'язково знайдеться лише рядки останньої дати, а зовнішній GROUP BYпіклується про зв’язки. Коли для одного і того ж користувача є дві записи на одну і ту ж дату, вона поверне ту саму, що має найвищу версію value.

SELECT t.username, t.date, MAX( t.value ) value
FROM your_table t
JOIN (
       SELECT username, MAX( date ) date
       FROM your_table
       GROUP BY username
) x ON ( x.username = t.username AND x.date = t.date )
GROUP BY t.username, t.date

1

Ви також можете використовувати аналітичну функцію Rank

    with temp as 
(
select username, date, RANK() over (partition by username order by date desc) as rnk from t
)
select username, rnk from t where rnk = 1

0
SELECT Username, date, value
 from MyTable mt
 inner join (select username, max(date) date
              from MyTable
              group by username) sub
  on sub.username = mt.username
   and sub.date = mt.date

Буде вирішено оновлену проблему. На великих таблицях це може не так добре працювати навіть при гарній індексації.


0
SELECT *
FROM ReportStatus c
inner join ( SELECT 
  MAX(Date) AS MaxDate
  FROM ReportStatus ) m
on  c.date = m.maxdate

0

Для Oracle сортує результат, встановлений у порядку зменшення, та приймає перший запис, тож ви отримаєте останню запис:

select * from mytable
where rownum = 1
order by date desc

0
SELECT DISTINCT Username, Dates,value 
FROM TableName
WHERE  Dates IN (SELECT  MAX(Dates) FROM TableName GROUP BY Username)


Username    Dates       value
bob         2010-02-02  1.2       
brad        2010-01-02  1.1       
fred        2010-01-03  1.0       

Це, ймовірно, не буде працювати, якби кілька користувачів замовляли замовлення в одну і ту ж дату; що робити, якщо брад і боб мали замовлення 2 січня?
AHiggins

Я групую за іменем користувача, щоб воно працювало, і результати будуть такими: Ім'я користувача Дати значення bob 2010-02-02 1,2 brad 2010-02-02 1,4 fred 2010-01-03 1,0
wa

0
SELECT t1.username, t1.date, value
FROM MyTable as t1
INNER JOIN (SELECT username, MAX(date)
            FROM MyTable
            GROUP BY username) as t2 ON  t2.username = t1.username AND t2.date = t1.date

4
Речення або два про реалізацію чи пояснення проходить довгий шлях до створення якісної відповіді.

0

Select * from table1 where lastest_date=(select Max(latest_date) from table1 where user=yourUserName)

Внутрішній запит поверне останню дату для поточного користувача, зовнішній запит витягне всі дані відповідно до внутрішнього результату запиту.


0

Я використовував цей спосіб, щоб взяти останній запис для кожного користувача, який я маю на своєму столі. Це був запит, щоб отримати останнє місце для продавця, як за останній час виявлено на пристроях КПК.

CREATE FUNCTION dbo.UsersLocation()
RETURNS TABLE
AS
RETURN
Select GS.UserID, MAX(GS.UTCDateTime) 'LastDate'
From USERGPS GS
where year(GS.UTCDateTime) = YEAR(GETDATE()) 
Group By GS.UserID
GO
select  gs.UserID, sl.LastDate, gs.Latitude , gs.Longitude
        from USERGPS gs
        inner join USER s on gs.SalesManNo = s.SalesmanNo 
        inner join dbo.UsersLocation() sl on gs.UserID= sl.UserID and gs.UTCDateTime = sl.LastDate 
        order by LastDate desc

0
SELECT * FROM TABEL1 WHERE DATE= (SELECT MAX(CREATED_DATE) FROM TABEL1)

Ласкаво просимо до StackOverflow та дякуємо за спробу допомогти. Відповіді, що стосуються лише коду, як і ваша, менш оцінюються порівняно з відповідями, що пояснюють рішення.
Yunnosch

Будь ласка, прочитайте цю інструкцію щодо відповіді для надання якісної відповіді.
thewaywewere

і. він не повертається до MAX для кожного імені користувача, лише до останнього одного рядка.
IrvineCAGу

0

Моя маленька збірка

  • самоврядування joinкраще, ніж вкладенеselect
  • але group byне дає вам primary keyкращогоjoin
  • цей ключ може бути наданий partition byспільно з first_value( docs )

Отже, ось запит:

виберіть
 т. *
з 
 Таблиця t внутрішнє з'єднання (
  виберіть окремий first_value (ID) over (розділ за порядком GroupColumn за описом DateColumn) як ID
  з табл
  де FilterColumn = 'значення'
 ) j на t.ID = j.ID

Плюси:

  • Фільтруйте дані за whereдопомогою оператора за допомогою будь-якого стовпця
  • select будь-які стовпці з відфільтрованих рядків

Мінуси:

  • Потрібен MS SQL Server, починаючи з 2012 року.

0

Я зробив дещо для своєї заявки так:

Нижче наведено запит:

select distinct i.userId,i.statusCheck, l.userName from internetstatus 
as i inner join login as l on i.userID=l.userID 
where nowtime in((select max(nowtime) from InternetStatus group by userID));    

0

Це схоже на одну з відповідей вище, але на мою думку, це набагато простіше і акуратніше. Крім того, показує гарне використання для оператора cross застосовувати. Для SQL Server 2005 і вище ...

select
    a.username,
    a.date,
    a.value,
from yourtable a
cross apply (select max(date) 'maxdate' from yourtable a1 where a.username=a1.username) b
where a.date=b.maxdate

0
SELECT MAX(DATE) AS dates 
FROM assignment  
JOIN paper_submission_detail ON  assignment.PAPER_SUB_ID = 
     paper_submission_detail.PAPER_SUB_ID 

1
Хоча цей код може вирішити питання, включаючи пояснення, як і чому це вирішує проблему, справді допоможе покращити якість вашої публікації та, ймовірно, призведе до збільшення кількості голосів. Пам'ятайте, що ви відповідаєте на запитання читачів у майбутньому, а не лише про людину, яка зараз задає питання. Будь ласка, відредагуйте свою відповідь, щоб додати пояснення та вказати, які обмеження та припущення застосовуються. З огляду
подвійний звуковий сигнал

-2

Це також має працювати для отримання всіх останніх записів для користувачів.

SELECT username, MAX(date) as Date, value
FROM MyTable
GROUP BY username, value

1
Привіт, стовпець значення повинен бути у групі за допомогою пункту.
Хуан Руїс де Кастілья

-4

Ви б використовували сукупні функції MAX та GROUP BY

SELECT username, MAX(date), value FROM tablename GROUP BY username, value

7
У вашій редакції буде вибрано лише випадковий value, а не той, що пов’язаний із MAX(date)рядком.
Елісон Р.

він дасть максимальну дату, але ім'я користувача та значення можуть бути не однаковими.
SKR
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.