Виберіть рядок із значенням "Макс" для стовпця


574

Таблиця:

UserId, Value, Date.

Я хочу отримати UserId, значення для макс (дати) для кожного UserId. Тобто значення для кожного UserId, який має останню дату. Чи є спосіб зробити це просто в SQL? (Переважно Oracle)

Оновлення: Вибачення за неоднозначність: мені потрібно отримати ВСІ UserIds. Але для кожного UserId лише той рядок, у якого цей користувач має останню дату.


21
Що робити, якщо існує декілька рядків із максимальним значенням дати для конкретного користувача?
Девід Олдрідж

Які ключові поля таблиці?
vamosrafa

деякі рішення нижче порівняно: sqlfiddle.com/#!4/6d4e81/1
Used_By_Already

1
@DavidAldridge, цей стовпець, ймовірно, унікальний.
Pacerier

Відповіді:


397

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

select userid,
       my_date,
       ...
from
(
select userid,
       my_date,
       ...
       max(my_date) over (partition by userid) max_my_date
from   users
)
where my_date = max_my_date

"Аналітичні функції рок"

Редагувати: Що стосується першого коментаря ...

"використання аналітичних запитів та самостійного приєднання перемагає мету аналітичних запитів"

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

"Вікно за замовчуванням в Oracle - з першого ряду в розділі до поточного"

Застереження про вікно застосовується лише за наявності наказу за пунктом. Якщо немає порядку за замовчуванням, жодне застереження про вікно не застосовується за замовчуванням, і жодне не може бути явно вказано.

Код працює.


38
Застосовуючи таблицю, що містить 8,8 мільйонів рядків, цей запит займав половину часу запитів у деяких інших високовідповідальних відповідях.
Дерек Махар

4
Хтось хотів опублікувати посилання на еквівалент цього MySQL, якщо такий є?
повторне

2
Не вдалося це повернути дублікати? Напр. якщо два ряди мають однаковий user_id та однакову дату (що трапляється як макс).
ястр

2
@jastr Я думаю, що це було визнано у питанні
Девід Олдрідж

3
Замість цього MAX(...) OVER (...)ви також можете використовувати ROW_NUMBER() OVER (...)(для топ-n-на групу) або RANK() OVER (...)(для найбільшої-n-на-групу).
MT0

441

Я бачу, що багато людей використовують підзапроси або інші функції для постачальника для цього, але я часто роблю такий запит без підзаписів наступним чином. Він використовує звичайний, стандартний SQL, тому він повинен працювати в будь-якій марці RDBMS.

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON (t1.UserId = t2.UserId AND t1."Date" < t2."Date")
WHERE t2.UserId IS NULL;

Іншими словами: витягніть рядок, t1звідки не існує іншого рядка з такою ж UserIdі більшою датою.

(Я ідентифікатор "Дата" ставлю в роздільники, оскільки це зарезервоване слово SQL.)

У разі, якщо t1."Date" = t2."Date"з'являється подвоєння. Зазвичай таблиці мають auto_inc(seq)ключ, наприклад id. Щоб уникнути подвоєння можна використовувати наступне:

SELECT t1.*
FROM mytable t1
  LEFT OUTER JOIN mytable t2
    ON t1.UserId = t2.UserId AND ((t1."Date" < t2."Date") 
         OR (t1."Date" = t2."Date" AND t1.id < t2.id))
WHERE t2.UserId IS NULL;

Re коментар від @Farhan:

Ось більш детальне пояснення:

Зовнішнє з'єднання намагається приєднатися t1до t2. За замовчуванням t1повертаються всі результати , а якщо є збіг t2, він також повертається. Якщо t2для даного рядка немає відповідності t1, то запит все одно повертає рядок t1і використовує NULLяк заповнювач для всіх t2стовпців '. Ось так працюють зовнішні з'єднання загалом.

Трюк у цьому запиті полягає в тому, щоб створити умову узгодження з'єднання таким, яке t2має відповідати однаковому userid та більшому date . Ідея полягає в тому, що якщо рядок існує в t2такому, що має більше date, то рядок у t1ньому порівняно з не може бути найбільшим dateдля цього userid. Але якщо немає відповідності - тобто, якщо немає рядка у t2більшому, dateніж рядок у t1-, ми знаємо, що рядок у t1був ряд з найбільшою dateдля даної userid.

У тих випадках (коли немає відповідності), стовпці t2будуть NULL- навіть стовпці, зазначені в умові приєднання. Тому ми використовуємо це WHERE t2.UserId IS NULL, оскільки ми шукаємо випадки, коли не було знайдено рядків із більшим dateдля даного userid.


7
Нічого собі Білл. Це найкреативніше рішення цієї проблеми, яку я бачив. Він досить ефективний на моєму досить великому наборі даних. Це впевнено перемагає багато інших рішень, які я бачив, або мої власні спроби вирішити цю складність.
Джастін Ноель

36
Застосовуючи таблицю, що містить 8,8 млн. Рядків, цей запит зайняв майже вдвічі більше, ніж у прийнятій відповіді.
Дерек Махар

16
@Derek: Оптимізація залежить від марки та версії RDBMS, а також наявності відповідних індексів, типів даних тощо.
Білл Карвін

7
У MySQL такий вид запиту фактично спричиняє перетворення циклу на результат декартового з'єднання між таблицями, в результаті чого настає час O (n ^ 2). Використання методу підзапиту замість цього скоротило час запиту з 2,0s до 0,003s. YMMV.
Джессі

1
Чи є спосіб пристосувати це до рядків, де дата є найбільшою датою, меншою або рівною даній користувачеві датою? Наприклад, якщо користувач вказує дату "23-ОКТ-2011", а таблиця містить рядки для "24-ОКТ-2011", "22-ОКТ-2011", "20-ОКТ-2011", тоді я хочу отримати "22-ОКТ-2011". Я чухав голову і читав цей фрагмент на деякий час ...
Cory Kendall,

164
SELECT userid, MAX(value) KEEP (DENSE_RANK FIRST ORDER BY date DESC)
  FROM table
  GROUP BY userid

3
У моїх тестах із використанням таблиці, що містить велику кількість рядків, це рішення зайняло приблизно вдвічі довше, ніж у прийнятій відповіді.
Дерек Махар

7
Покажіть свій тест, будь ласка
Роб ван Війк

Я підтверджую, що це набагато швидше, ніж інші рішення
tamersalama

5
біда в тому, що він не повертає повний запис
Used_By_Already

@ user2067753 Ні, він не повертає повний запис. Ви можете використовувати той самий MAX () .. KEEP .. вираз у кількох стовпцях, щоб ви могли вибрати всі потрібні стовпці. Але це незручно, якщо ви хочете отримати велику кількість стовпців і вважаєте за краще використовувати SELECT *.
Дейв Коста

51

Я не знаю ваших точних назв стовпців, але це було б приблизно таке:

    вибрати userid, значення
      від користувачів u1
     де дата = (виберіть макс. (дата)
                     від користувачів u2
                    де u1.userid = u2.userid)

3
Напевно, не дуже ефективно, Стів.
Девід Олдрідж

7
Ви, ймовірно, недооцінюєте оптимізатор запитів Oracle.
Rafał Dowgird

3
Зовсім ні. Це майже напевно буде реалізовано як повне сканування з вкладеним циклом приєднання для отримання дат. Ви говорите про логічні io в порядку, що в 4 рази перевищує кількість рядків у таблиці, і бути страшним за нетривіальні обсяги даних.
Девід Олдрідж

4
FYI, "Не ефективний, але працює" - це те саме, що "Працює, але не є ефективним". Коли ми відмовились від ефективної як дизайнерської мети?
Девід Олдрідж

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

35

Не перебуваючи на роботі, у мене немає Oracle під рукою, але, схоже, я нагадую, що Oracle дозволяє зіставити декілька стовпців у пункті IN, що повинно принаймні уникати параметрів, що використовують корельований підзапит, що рідко є добрим ідея.

Можливо, щось подібне (не пам'ятаю, чи слід у списках стовпців скористатися дужками чи ні):

SELECT * 
FROM MyTable
WHERE (User, Date) IN
  ( SELECT User, MAX(Date) FROM MyTable GROUP BY User)

EDIT: Просто спробував це по-справжньому:

SQL> create table MyTable (usr char(1), dt date);
SQL> insert into mytable values ('A','01-JAN-2009');
SQL> insert into mytable values ('B','01-JAN-2009');
SQL> insert into mytable values ('A', '31-DEC-2008');
SQL> insert into mytable values ('B', '31-DEC-2008');
SQL> select usr, dt from mytable
  2  where (usr, dt) in 
  3  ( select usr, max(dt) from mytable group by usr)
  4  /

U DT
- ---------
A 01-JAN-09
B 01-JAN-09

Так це працює, хоча деякі нові речі, згадані в інших місцях, можуть бути більш ефективними.


4
Це добре працює і на PostgreSQL. І мені подобається простота і загальність - підзапит говорить "Ось мої критерії", зовнішній запит говорить "А ось деталі, які я хочу побачити". +1.
j_random_hacker

13

Я знаю, що ви попросили Oracle, але в SQL 2005 ми зараз використовуємо це:


-- Single Value
;WITH ByDate
AS (
SELECT UserId, Value, ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) RowNum
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE RowNum = 1

-- Multiple values where dates match
;WITH ByDate
AS (
SELECT UserId, Value, RANK() OVER (PARTITION BY UserId ORDER BY Date DESC) Rnk
FROM UserDates
)
SELECT UserId, Value
FROM ByDate
WHERE Rnk = 1

7

У мене немає Oracle, щоб перевірити його, але найефективнішим рішенням є використання аналітичних запитів. Це має виглядати приблизно так:

SELECT DISTINCT
    UserId
  , MaxValue
FROM (
    SELECT UserId
      , FIRST (Value) Over (
          PARTITION BY UserId
          ORDER BY Date DESC
        ) MaxValue
    FROM SomeTable
  )

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

Якщо ви хочете дізнатися про аналітичні запити, пропоную прочитати http://www.orafaq.com/node/55 та http://www.akadia.com/services/ora_analytic_functions.html . Ось короткий підсумок.

Під аналітичними запитами капота сортуйте весь набір даних, а потім обробляйте їх послідовно. Під час його обробки ви розподіляєте набір даних за певними критеріями, а потім для кожного рядка дивитесь на якесь вікно (за замовчуванням перше значення в розділі до поточного рядка - цей типовий параметр також є найбільш ефективним) і може обчислити значення, використовуючи кількість аналітичних функцій (перелік яких дуже схожий на сукупність функцій).

У цьому випадку ось що робить внутрішній запит. Весь набір даних відсортований за UserId та датою DESC. Потім він обробляє його за один прохід. Для кожного рядка ви повертаєте UserId та першу дату, побачену для цього UserId (оскільки дати відсортовані DESC, це максимальна дата). Це дає вам свою відповідь з подвоєними рядками. Тоді зовнішній DISTINCT стискає дублікати.

Це не особливо вражаючий приклад аналітичних запитів. Для набагато більшого виграшу розглянемо взяти таблицю фінансових надходжень та розрахунок для кожного користувача та отримання, загальний обсяг того, що вони заплатили. Аналітичні запити вирішують це ефективно. Інші рішення менш ефективні. Тому вони є частиною стандарту SQL 2003 року. (На жаль, у Postgres їх ще немає. Grrr ...)


Вам також потрібно повернути значення дати, щоб повністю відповісти на питання. Якщо це означає ще один пункт first_value, я б припустив, що рішення є складнішим, ніж воно повинно бути, і аналітичний метод, заснований на max (date), читається краще.
Девід Олдрідж

У запитанні запитання нічого не сказано про повернення дати. Це можна зробити, додавши ще ПЕРШУ (Дату) або просто запитуючи дату та змінивши зовнішній запит на GROUP BY. Я використовую перший і очікую, що оптимізатор обчислить обидва за один прохід.
користувач11318

"Заява питання нічого не говорить про повернення дати" ... так, ви праві. Вибачте. Але додавання більше FIRST_VALUE застережень стане досить заплутаним досить швидко. Це єдине сортування вікон, але якщо у вас було 20 стовпців для повернення для цього рядка, ви написали багато коду, щоб пройти його.
Девід Олдрідж

Мені також здається, що це рішення є недетермінованим для даних, коли один користувач має декілька рядків, які мають максимальну дату та різні значення. Хоча більше помилок у питанні, ніж відповіді.
Девід Олдрідж

1
Я згоден, це болісно багатослівно. Однак, як правило, це не так з SQL? І ви маєте рацію, що рішення не є детермінованим. Існує кілька способів вирішити зв'язки, а іноді кожен - це те, що ви хочете.
користувач11318

6

Чи не було б якісне застереження бути і найпростішим, і найкращим?

select userid, my_date, ...
from users
qualify rank() over (partition by userid order by my_date desc) = 1

Для контексту, на Teradata тут пристосований тест на розмір працює у 17-х роках із цією КВАЛІФІЙНОю версією та у 23-х з "вбудованим видом" / рішенням Олдріджа №1.


1
Це найкраща відповідь на мій погляд. Однак будьте обережні з rank()функцією в ситуаціях, коли є зв’язки. Ви можете закінчити більше rank=1. Краще використовувати, row_number()якщо ви дійсно хочете повернути лише один запис.
cartbeforehorse

1
Також майте на увазі, що QUALIFYстаття є специфічною для Терадати. В Oracle (принаймні) ви повинні вкладати свій запит і фільтрувати за допомогою WHEREпункту на загортанні select willing (який, мабуть, міг би вразити ефективність).
cartbeforehorse

5

У цьому випадку Oracle 12c+ви можете використовувати Top n запитів разом з аналітичною функцією, rankщоб досягти цього дуже стисло без підзапитів:

select *
from your_table
order by rank() over (partition by user_id order by my_date desc)
fetch first 1 row with ties;

Вищезазначене повертає всі рядки з максимумом my_date на користувача.

Якщо ви хочете тільки один рядок з максимальною датою, а потім замінити rankз row_number:

select *
from your_table
order by row_number() over (partition by user_id order by my_date desc)
fetch first 1 row with ties; 

5

Використовуйте ROW_NUMBER()для призначення унікального рейтингу за спаданням Dateдля кожного UserId, а потім фільтруйте до першого рядка для кожного UserId(тобто ROW_NUMBER= 1).

SELECT UserId, Value, Date
FROM (SELECT UserId, Value, Date,
        ROW_NUMBER() OVER (PARTITION BY UserId ORDER BY Date DESC) rn
      FROM users) u
WHERE rn = 1;

5

З PostgreSQL 8.4 або новішою версією ви можете використовувати це:

select user_id, user_value_1, user_value_2
  from (select user_id, user_value_1, user_value_2, row_number()
          over (partition by user_id order by user_date desc) 
        from users) as r
  where r.row_number=1

3

я те, що ти повинен зробити цей варіант попереднього запиту:

SELECT UserId, Value FROM Users U1 WHERE 
Date = ( SELECT MAX(Date)    FROM Users where UserId = U1.UserId)

3
Select  
   UserID,  
   Value,  
   Date  
From  
   Table,  
   (  
      Select  
          UserID,  
          Max(Date) as MDate  
      From  
          Table  
      Group by  
          UserID  
    ) as subQuery  
Where  
   Table.UserID = subQuery.UserID and  
   Table.Date = subQuery.mDate  

3

Просто довелося написати «живий» приклад на роботі :)

Цей підтримує декілька значень для UserId в одну і ту ж дату.

Стовпці: UserId, значення, дата

SELECT
   DISTINCT UserId,
   MAX(Date) OVER (PARTITION BY UserId ORDER BY Date DESC),
   MAX(Values) OVER (PARTITION BY UserId ORDER BY Date DESC)
FROM
(
   SELECT UserId, Date, SUM(Value) As Values
   FROM <<table_name>>
   GROUP BY UserId, Date
)

Ви можете використовувати FIRST_VALUE замість MAX і переглянути його в плані пояснення. Я не мав часу грати з цим.

Звичайно, якщо шукати величезні таблиці, напевно, краще, якщо ви використовуєте ПОЛІ підказки у своєму запиті.


3
select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))

2

Я думаю, щось подібне. (Пробачте мене за будь-які синтаксичні помилки; я звик використовувати HQL в цей момент!)

EDIT: Також неправильно прочитайте питання! Виправлено запит ...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)

Не відповідає умові "для кожного UserId"
Девід Олдрідж,

Де б це не вдалося? Для кожного UserID в Користувачах буде гарантовано повернення щонайменше одного рядка, що містить цей UserID. Або я десь пропускаю спеціальний випадок?
jdmichal

2

(T-SQL) Спочатку отримайте всіх користувачів та їх максимум. Об'єднайтеся з таблицею, щоб знайти відповідні значення для користувачів на мандатах.

create table users (userid int , value int , date datetime)
insert into users values (1, 1, '20010101')
insert into users values (1, 2, '20020101')
insert into users values (2, 1, '20010101')
insert into users values (2, 3, '20030101')

select T1.userid, T1.value, T1.date 
    from users T1,
    (select max(date) as maxdate, userid from users group by userid) T2    
    where T1.userid= T2.userid and T1.date = T2.maxdate

результати:

userid      value       date                                    
----------- ----------- -------------------------- 
2           3           2003-01-01 00:00:00.000
1           2           2002-01-01 00:00:00.000

2

Відповідь тут лише Oracle. Ось трохи складніша відповідь у всіх SQL:

Хто має найкращий загальний результат домашнього завдання (максимальна сума балів за домашнє завдання)?

SELECT FIRST, LAST, SUM(POINTS) AS TOTAL
FROM STUDENTS S, RESULTS R
WHERE S.SID = R.SID AND R.CAT = 'H'
GROUP BY S.SID, FIRST, LAST
HAVING SUM(POINTS) >= ALL (SELECT SUM (POINTS)
FROM RESULTS
WHERE CAT = 'H'
GROUP BY SID)

І більш складний приклад, який потребує деяких пояснень, на які я не маю часу atm:

Назвіть книгу (ISBN та заголовок), яка найбільш популярна у 2008 році, тобто запозичена найчастіше у 2008 році.

SELECT X.ISBN, X.title, X.loans
FROM (SELECT Book.ISBN, Book.title, count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title) X
HAVING loans >= ALL (SELECT count(Loan.dateTimeOut) AS loans
FROM CatalogEntry Book
LEFT JOIN BookOnShelf Copy
ON Book.bookId = Copy.bookId
LEFT JOIN (SELECT * FROM Loan WHERE YEAR(Loan.dateTimeOut) = 2008) Loan 
ON Copy.copyId = Loan.copyId
GROUP BY Book.title);

Сподіваюся, це допомагає (комусь) .. :)

З повагою, Гусю


Прийнята відповідь не "Тільки Oracle" - це стандартний SQL (підтримується багатьма СУБД)
a_horse_with_no_name

2

Якщо припустити, що дата унікальна для даного UserID, ось деякі TSQL:

SELECT 
    UserTest.UserID, UserTest.Value
FROM UserTest
INNER JOIN
(
    SELECT UserID, MAX(Date) MaxDate
    FROM UserTest
    GROUP BY UserID
) Dates
ON UserTest.UserID = Dates.UserID
AND UserTest.Date = Dates.MaxDate 

2

Я досить пізно до партії, але наступний хак буде перевершувати як корельовані підзапити, так і будь-яку функцію аналітики, але має одне обмеження: значення повинні перетворюватися на рядки. Так воно працює для дат, цифр та інших рядків. Код виглядає не дуже добре, але профіль виконання чудовий.

select
    userid,
    to_number(substr(max(to_char(date,'yyyymmdd') || to_char(value)), 9)) as value,
    max(date) as date
from 
    users
group by
    userid

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


Це хороший план виконання порівняно з більшістю, але застосування всіх цих хитрощів до кількох полів було б втомливим і може працювати проти цього. Але дуже цікаво - спасибі. дивіться sqlfiddle.com/#!4/2749b5/23
Б / у_By_Авже

Ви маєте рацію, це може стати втомливим, тому це потрібно робити лише тоді, коли цього вимагає виконання запиту. Таке часто трапляється зі сценаріями ETL.
aLevelOfIndirection

це дуже приємно. зробив щось подібне, використовуючи LISTAGG, але виглядає некрасиво. postgres має кращий альтернативний варіант, використовуючи array_agg. дивіться мою відповідь :)
Бруно Кальза

1
select userid, value, date
  from thetable t1 ,
       ( select t2.userid, max(t2.date) date2 
           from thetable t2 
          group by t2.userid ) t3
 where t3.userid t1.userid and
       t3.date2 = t1.date

ІМХО це працює. HTH


1

Я думаю, що це має спрацювати?

Select
T1.UserId,
(Select Top 1 T2.Value From Table T2 Where T2.UserId = T1.UserId Order By Date Desc) As 'Value'
From
Table T1
Group By
T1.UserId
Order By
T1.UserId

1

Спершу спробуйте я неправильно прочитав запитання, виходячи з головної відповіді, ось повний приклад з правильними результатами:

CREATE TABLE table_name (id int, the_value varchar(2), the_date datetime);

INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'a','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(1 ,'b','2/2/2002');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'c','1/1/2000');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'d','3/3/2003');
INSERT INTO table_name (id,the_value,the_date) VALUES(2 ,'e','3/3/2003');

-

  select id, the_value
      from table_name u1
      where the_date = (select max(the_date)
                     from table_name u2
                     where u1.id = u2.id)

-

id          the_value
----------- ---------
2           d
2           e
1           b

(3 row(s) affected)

1

Це також допоможе подвоїти копії (повернути один рядок для кожного user_id):

SELECT *
FROM (
  SELECT u.*, FIRST_VALUE(u.rowid) OVER(PARTITION BY u.user_id ORDER BY u.date DESC) AS last_rowid
  FROM users u
) u2
WHERE u2.rowid = u2.last_rowid

1

Тільки тестував це, і, здається, працює над таблицею журналу

select ColumnNames, max(DateColumn) from log  group by ColumnNames order by 1 desc

1

Це повинно бути таким же простим, як:

SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)

1

Рішення для MySQL, який не має понять розділу KEEP, DENSE_RANK.

select userid,
       my_date,
       ...
from
(
select @sno:= case when @pid<>userid then 0
                    else @sno+1
    end as serialnumber, 
    @pid:=userid,
       my_Date,
       ...
from   users order by userid, my_date
) a
where a.serialnumber=0

Довідка: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html


Це не працює "і для інших БД ". Це працює лише на MySQL і, можливо, на SQL Server, оскільки він має схожу концепцію змінних. Він точно не працюватиме на Oracle, Postgres, DB2, Derby, H2, HSQLDB, Vertica, Greenplum. Крім того, прийнята відповідь - це стандартний ANSI SQL (який знає лише MySQL не підтримує)
a_horse_with_no_name

коня, я думаю, ти маєш рацію. Я не маю знань про інші БД або ANSI. Моє рішення здатне вирішити проблему в MySQL, який не має належної підтримки для ANSI SQL для її вирішення стандартним способом.
Бен Лін

1

Якщо ви використовуєте Postgres, ви можете використовувати, array_aggяк

SELECT userid,MAX(adate),(array_agg(value ORDER BY adate DESC))[1] as value
FROM YOURTABLE
GROUP BY userid

Я не знайомий з Oracle. Це те, що я придумав

SELECT 
  userid,
  MAX(adate),
  SUBSTR(
    (LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)),
    0,
    INSTR((LISTAGG(value, ',') WITHIN GROUP (ORDER BY adate DESC)), ',')-1
  ) as value 
FROM YOURTABLE
GROUP BY userid 

Обидва запити повертають ті ж результати, що і прийнята відповідь. Дивіться SQLFiddles:

  1. Прийнята відповідь
  2. Моє рішення з Postgres
  3. Моє рішення з Oracle

0

Якщо (UserID, дата) унікальна, тобто жодна дата не з’являється двічі для одного користувача, тоді:

select TheTable.UserID, TheTable.Value
from TheTable inner join (select UserID, max([Date]) MaxDate
                          from TheTable
                          group by UserID) UserMaxDate
     on TheTable.UserID = UserMaxDate.UserID
        TheTable.[Date] = UserMaxDate.MaxDate;

Я вважаю, що вам потрібно приєднатись і до UserID
Tom H,

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