Извлечь строку, которая имеет значение Max для столбца

576

Таблица:

UserId, Value, Date.

Я хочу получить UserId, значение для max (Date) для каждого UserId. То есть значение для каждого идентификатора пользователя, который имеет самую последнюю дату. Есть ли способ сделать это просто в SQL? (Желательно Oracle)

Обновление: извинения за любую двусмысленность: мне нужно получить ВСЕ UserIds. Но для каждого UserId только та строка, где у этого пользователя самая последняя дата.

Umang
источник
21
Что если несколько строк имеют максимальное значение даты для определенного идентификатора пользователя?
Дэвид Олдридж
Каковы ключевые поля таблицы?
Вамосрафа
ниже приведены некоторые решения: sqlfiddle.com/#!4/6d4e81/1
Used_By_Already
1
@DavidAldridge, этот столбец, вероятно, уникален.
Pacerier

Ответы:

398

Это извлечет все строки, для которых значение столбца my_date равно максимальному значению my_date для этого идентификатора пользователя. Это может извлечь несколько строк для идентификатора пользователя, где максимальная дата находится в нескольких строках.

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 - от первой строки в разделе до текущей»

Оконное предложение применимо только при наличии предложения order by. Без указания по порядку, по умолчанию не применяется ни одно оконное предложение, и ни одно из них не может быть указано явно.

Код работает.

Дэвид Олдридж
источник
39
Применительно к таблице, имеющей 8,8 миллиона строк, этот запрос занимал половину времени запросов в некоторых других высоко голосуемых ответах.
Дерек Махар
4
Кто-нибудь хочет опубликовать ссылку на MySQL эквивалент этого, если таковой имеется?
благоухающий
2
Не может ли это вернуть дубликаты? Например. если две строки имеют одинаковый user_id и одну и ту же дату (что является максимальным).
Jastr
2
@jastr Я думаю, что это было признано в вопросе
Дэвид Олдридж
3
Вместо этого MAX(...) OVER (...)вы также можете использовать ROW_NUMBER() OVER (...)(для лучших n-на-группу) или RANK() OVER (...)(для наибольшего-n-на-группу).
MT0
441

Я вижу, что многие люди используют для этого подзапросы или другие специфичные для поставщика функции, но я часто делаю этот тип запросов без подзапросов следующим образом. Он использует простой стандартный SQL, поэтому он должен работать в любой марке СУБД.

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
Ух ты, Билл. Это самое креативное решение этой проблемы, которое я видел. Это довольно производительно на моем довольно большом наборе данных. Это наверняка превосходит многие другие решения, которые я видел, или мои собственные попытки решить эту проблему.
Джастин Ноэль
37
Применительно к таблице с 8,8 миллионами строк этот запрос занимал почти вдвое больше времени, чем в принятом ответе.
Дерек Махар
16
@Derek: Оптимизация зависит от бренда и версии СУБД, а также от наличия соответствующих индексов, типов данных и т. Д.
Билл Карвин,
7
В MySQL этот тип запроса, по-видимому, фактически заставляет его зацикливаться на результате декартового объединения таблиц, что приводит к времени O (n ^ 2). Использование метода подзапроса вместо этого уменьшило время запроса с 2,0 до 0,003 с. YMMV.
Джесси
1
Есть ли способ адаптировать это для соответствия строк, где дата наибольшая дата меньше или равна заданной пользователем дате? Например, если пользователь указывает дату «23-ОКТ-2011», а таблица содержит строки для «24-ОКТ-2011», «22-ОКТ-2011», «20-ОКТ-2011», то я хочу получить "22-ОКТ-2011". Я почесал голову и некоторое время читал этот фрагмент ...
Кори Кендалл
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

Я не знаю ваших точных имен столбцов, но это будет что-то вроде этого:

    выберите идентификатор пользователя, значение
      от пользователей u1
     где дата = (выберите максимум (дата)
                     от пользователей u2
                    где u1.userid = u2.userid)
Стив К
источник
3
Наверное, не очень эффективно, Стив.
Дэвид Олдридж
7
Вы, вероятно, недооцениваете оптимизатор запросов Oracle.
Рафал Доугирд
3
Не за что. Это почти наверняка будет реализовано как полное сканирование с вложенным циклом, чтобы получить даты. Вы говорите о логических операциях ввода-вывода в 4 раза превышающем количество строк в таблице, и вам страшно за нетривиальные объемы данных.
Дэвид Олдридж
4
К вашему сведению, «Не эффективно, но работает» - это то же самое, что «Работает, но не эффективно». Когда мы отказались от эффективной цели дизайна?
Дэвид Олдридж
6
+1, потому что, когда ваши таблицы данных не являются миллионами строк в длину, это самое понятное решение. когда у вас есть несколько разработчиков всех уровней квалификации, которые изменяют код, понятность важнее доли секунды, что незаметно.
n00b
35

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

Возможно, что-то вроде этого (не помню, нужно ли заключать в скобки список столбцов):

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

РЕДАКТИРОВАТЬ: Просто попробовал это по-настоящему:

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
mancaus
источник
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 и первую Date, увиденную для этого UserId (поскольку даты отсортированы DESC, это максимальная дата). Это дает вам ваш ответ с дублированными строками. Тогда внешний DISTINCT сдавливает дубликаты.

Это не особенно впечатляющий пример аналитических запросов. Для гораздо большего выигрыша рассмотрите возможность получения таблицы финансовых квитанций и расчета для каждого пользователя и квитанции, промежуточной суммы того, что они заплатили. Аналитические запросы решают это эффективно. Другие решения менее эффективны. Именно поэтому они являются частью стандарта SQL 2003 года. (К сожалению, у Postgres их пока нет. Гррр ...)

user11318
источник
Вам также необходимо вернуть значение даты, чтобы полностью ответить на вопрос. Если это означает еще одно предложение first_value, то я бы предположил, что решение является более сложным, чем должно быть, и аналитический метод, основанный на max (date), выглядит лучше
Дэвид Олдридж
В постановке вопроса ничего не говорится о возврате даты. Вы можете сделать это либо добавив еще один FIRST (Date), либо просто запросив Date и изменив внешний запрос на GROUP BY. Я бы использовал первое и ожидал, что оптимизатор рассчитает оба за один проход.
user11318 23.09.08
«В постановке вопроса ничего не говорится о возврате даты» ... да, вы правы. Сожалею. Но добавление большего количества предложений FIRST_VALUE может стать довольно грязным. Это сортировка по одному окну, но если у вас было 20 столбцов, которые нужно вернуть для этой строки, то вы написали много кода для просмотра.
Дэвид Олдридж
Мне также приходит в голову, что это решение недетерминировано для данных, в которых один идентификатор пользователя содержит несколько строк с максимальной датой и разными ЗНАЧЕНИЯМИ. Больше ошибка в вопросе, чем ответ, хотя.
Дэвид Олдридж
1
Я согласен, это мучительно многословно. Однако разве это не так в случае с SQL? И вы правы, что решение недетерминировано. Есть несколько способов справиться со связями, и иногда каждый - то, что вы хотите.
user11318 23.09.08
6

Не будет ли предложение QUALIFY одновременно и самым простым, и лучшим?

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

Для контекста, на Teradata здесь тест на приличный размер этого теста выполняется в 17-х годах с этой версией QUALIFY и в 23-х с решением «inline view» / Aldridge # 1.

WCW
источник
1
Это лучший ответ на мой взгляд. Однако будьте осторожны с этой rank()функцией в ситуациях, когда есть связи. Вы можете получить более одного rank=1. Лучше использовать, row_number()если вы действительно хотите вернуть только одну запись.
cartbeforehorse
1
Также имейте в виду, что это QUALIFYпредложение относится к Teradata. В Oracle (по крайней мере) вы должны вкладывать свой запрос и фильтровать, используя WHEREпредложение в операторе select обтекания (который, я думаю, может повлиять на производительность).
верховая езда
6

С 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
Cito
источник
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;

Вышеприведенное возвращает все строки с max 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;
markusk
источник
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  
Aheho
источник
3

Просто нужно было написать «живой» пример на работе :)

Этот поддерживает несколько значений для 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 и посмотреть его в плане объяснения. У меня не было времени поиграть с ним.

Конечно, при поиске в огромных таблицах, вероятно, лучше использовать в запросе ПОЛНЫЕ подсказки.

Truper
источник
3
select VALUE from TABLE1 where TIME = 
   (select max(TIME) from TABLE1 where DATE= 
   (select max(DATE) from TABLE1 where CRITERIA=CRITERIA))
nouky
источник
2

Я думаю что-то вроде этого. (Простите за любые синтаксические ошибки; я привык использовать HQL на этом этапе!)

РЕДАКТИРОВАТЬ: Также неправильно прочитал вопрос! Исправил запрос ...

SELECT UserId, Value
FROM Users AS user
WHERE Date = (
    SELECT MAX(Date)
    FROM Users AS maxtest
    WHERE maxtest.UserId = user.UserId
)
jdmichal
источник
Не соответствует условию «для каждого идентификатора пользователя»
Дэвид Олдридж,
Где это потерпит неудачу? Для каждого UserID в Users будет гарантировано, что будет возвращена хотя бы одна строка, содержащая этот UserID. Или я где-то пропускаю особый случай?
jdmichal
2

(T-SQL) Сначала получите всех пользователей и их maxdate. Присоединитесь к таблице, чтобы найти соответствующие значения для пользователей на максимальных значениях.

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)

И более сложный пример, требующий пояснения, для которого у меня нет времени:

Укажите книгу (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

Предполагая, что Date уникален для данного идентификатора пользователя, вот несколько 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

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

aLevelOfIndirection
источник
Это хороший план выполнения по сравнению с большинством, но применение всех этих трюков к нескольким полям будет утомительным и может сработать против него. Но очень интересно - спасибо. см. sqlfiddle.com/#!4/2749b5/23
Used_By_Already
Вы правы, это может стать утомительным, поэтому это следует делать только тогда, когда этого требует производительность запроса. Такое часто случается со сценариями ETL.
aLevelOfIndirection
это очень мило. сделал что-то подобное, используя LISTAGG, но выглядит ужасно. У postgres есть лучший альтернативный вариант с использованием array_agg. смотрите мой ответ :)
Bruno Calza
2

Если вы используете 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
Бруно Кальза
источник
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

ИМХО это работает. НТН

Жолт Ботыкай
источник
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
GateKiller
источник
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)
KyleLanser
источник
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
na43251
источник
1

Только что проверил это, и, кажется, работает на столе регистрации

select ColumnNames, max(DateColumn) from log  group by ColumnNames order by 1 desc
Mauro
источник
1

Это должно быть так просто, как:

SELECT UserId, Value
FROM Users u
WHERE Date = (SELECT MAX(Date) FROM Users WHERE UserID = u.UserID)
Valerion
источник
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 для ее стандартного решения.
Бен Лин
0

Если (UserID, Date) является уникальным, то есть ни одна дата не появляется дважды для одного и того же пользователя, тогда:

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;
finnw
источник
Я считаю, что вам нужно присоединиться и по идентификатору пользователя
Том Х,
0
select   UserId,max(Date) over (partition by UserId) value from users;
Амитабха
источник
2
Это вернет все строки, а не только одну строку на пользователя.
Джон Хеллер