Таблица:
UserId, Value, Date.
Я хочу получить UserId, значение для max (Date) для каждого UserId. То есть значение для каждого идентификатора пользователя, который имеет самую последнюю дату. Есть ли способ сделать это просто в SQL? (Желательно Oracle)
Обновление: извинения за любую двусмысленность: мне нужно получить ВСЕ UserIds. Но для каждого UserId только та строка, где у этого пользователя самая последняя дата.
Ответы:
Это извлечет все строки, для которых значение столбца my_date равно максимальному значению my_date для этого идентификатора пользователя. Это может извлечь несколько строк для идентификатора пользователя, где максимальная дата находится в нескольких строках.
«Аналитические функции рока»
Редактировать: Что касается первого комментария ...
«Использование аналитических запросов и самостоятельное объединение наносят ущерб цели аналитических запросов»
В этом коде нет самостоятельного объединения. Вместо этого на результат встроенного представления помещается предикат, который содержит аналитическую функцию - совершенно другой вопрос и совершенно стандартная практика.
«Окно по умолчанию в Oracle - от первой строки в разделе до текущей»
Оконное предложение применимо только при наличии предложения order by. Без указания по порядку, по умолчанию не применяется ни одно оконное предложение, и ни одно из них не может быть указано явно.
Код работает.
источник
MAX(...) OVER (...)
вы также можете использоватьROW_NUMBER() OVER (...)
(для лучших n-на-группу) илиRANK() OVER (...)
(для наибольшего-n-на-группу).Я вижу, что многие люди используют для этого подзапросы или другие специфичные для поставщика функции, но я часто делаю этот тип запросов без подзапросов следующим образом. Он использует простой стандартный SQL, поэтому он должен работать в любой марке СУБД.
Другими словами: получить строку, из
t1
которой не существует другой строки с такой жеUserId
и большей датой.(Я поместил идентификатор «Дата» в разделители, потому что это зарезервированное слово SQL.)
В случае, если
t1."Date" = t2."Date"
удвоение появляется. Обычно таблицы имеютauto_inc(seq)
ключ, напримерid
. Во избежание дублирования можно использовать следующее: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
.источник
источник
Я не знаю ваших точных имен столбцов, но это будет что-то вроде этого:
источник
Не будучи на работе, у меня нет Oracle для передачи, но я, кажется, напоминаю, что Oracle позволяет сопоставлять несколько столбцов в предложении IN, что должно, по крайней мере, избегать опций, использующих коррелированный подзапрос, что редко является хорошим идея.
Возможно, что-то вроде этого (не помню, нужно ли заключать в скобки список столбцов):
РЕДАКТИРОВАТЬ: Просто попробовал это по-настоящему:
Так что это работает, хотя некоторые из новых вещей, упомянутых в другом месте, могут быть более производительными.
источник
Я знаю, что вы просили Oracle, но в SQL 2005 мы теперь используем это:
источник
У меня нет Oracle для его тестирования, но самое эффективное решение - использовать аналитические запросы. Это должно выглядеть примерно так:
Я подозреваю, что вы можете избавиться от внешнего запроса и поставить разные на внутренний, но я не уверен. В то же время я знаю, что это работает.
Если вы хотите узнать об аналитических запросах, я бы предложил прочитать 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 их пока нет. Гррр ...)
источник
Не будет ли предложение QUALIFY одновременно и самым простым, и лучшим?
Для контекста, на Teradata здесь тест на приличный размер этого теста выполняется в 17-х годах с этой версией QUALIFY и в 23-х с решением «inline view» / Aldridge # 1.
источник
rank()
функцией в ситуациях, когда есть связи. Вы можете получить более одногоrank=1
. Лучше использовать,row_number()
если вы действительно хотите вернуть только одну запись.QUALIFY
предложение относится к Teradata. В Oracle (по крайней мере) вы должны вкладывать свой запрос и фильтровать, используяWHERE
предложение в операторе select обтекания (который, я думаю, может повлиять на производительность).С PostgreSQL 8.4 или новее вы можете использовать это:
источник
В
Oracle 12c+
, вы можете использовать Top n запросов вместе с аналитической функцией,rank
чтобы достичь этого очень кратко без подзапросов:Вышеприведенное возвращает все строки с max my_date для каждого пользователя.
Если вы хотите только одну строку с максимальной датой, а затем заменить
rank
сrow_number
:источник
Используйте,
ROW_NUMBER()
чтобы назначить уникальное ранжирование по убываниюDate
для каждогоUserId
, а затем выполнить фильтрацию по первой строке для каждогоUserId
(т. Е.ROW_NUMBER
= 1).источник
Я думаю, что вы должны сделать этот вариант к предыдущему запросу:
источник
источник
Просто нужно было написать «живой» пример на работе :)
Этот поддерживает несколько значений для UserId на одну и ту же дату.
Столбцы: идентификатор пользователя, значение, дата
Вы можете использовать FIRST_VALUE вместо MAX и посмотреть его в плане объяснения. У меня не было времени поиграть с ним.
Конечно, при поиске в огромных таблицах, вероятно, лучше использовать в запросе ПОЛНЫЕ подсказки.
источник
источник
Я думаю что-то вроде этого. (Простите за любые синтаксические ошибки; я привык использовать HQL на этом этапе!)
РЕДАКТИРОВАТЬ: Также неправильно прочитал вопрос! Исправил запрос ...
источник
(T-SQL) Сначала получите всех пользователей и их maxdate. Присоединитесь к таблице, чтобы найти соответствующие значения для пользователей на максимальных значениях.
Результаты:
источник
Ответ здесь только Oracle. Вот немного более сложный ответ во всех SQL:
У кого лучший общий результат домашней работы (максимальная сумма очков за домашнюю работу)?
И более сложный пример, требующий пояснения, для которого у меня нет времени:
Укажите книгу (ISBN и название), которая наиболее популярна в 2008 году, т. Е. Чаще всего заимствована в 2008 году.
Надеюсь, что это помогает (любой) .. :)
С уважением, Гус
источник
Предполагая, что Date уникален для данного идентификатора пользователя, вот несколько TSQL:
источник
Я довольно опоздал на вечеринку, но следующий хак превзойдет как коррелированные подзапросы, так и любую аналитическую функцию, но имеет одно ограничение: значения должны преобразовываться в строки. Так что это работает для дат, чисел и других строк. Код не выглядит хорошо, но профиль исполнения отличный.
Причина того, что этот код работает так хорошо, состоит в том, что ему нужно только один раз отсканировать таблицу. Он не требует каких-либо индексов и, что самое важное, не нуждается в сортировке таблицы, как это делают большинство аналитических функций. Индексы помогут, хотя, если вам нужно отфильтровать результат для одного идентификатора пользователя.
источник
Если вы используете Postgres, вы можете использовать
array_agg
какЯ не знаком с Oracle. Это то, что я придумал
Оба запроса возвращают те же результаты, что и принятый ответ. Смотрите SQLFiddles:
источник
ИМХО это работает. НТН
источник
Я думаю, что это должно работать?
источник
Сначала попробуйте неправильно прочитать вопрос, следуя верхнему ответу, вот полный пример с правильными результатами:
-
-
источник
Это также позаботится о дубликатах (возвращает одну строку для каждого user_id):
источник
Только что проверил это, и, кажется, работает на столе регистрации
источник
Это должно быть так просто, как:
источник
Решение для MySQL, которое не имеет понятия раздела KEEP, DENSE_RANK.
Ссылка: http://benincampus.blogspot.com/2013/08/select-rows-which-have-maxmin-value-in.html
источник
Если (UserID, Date) является уникальным, то есть ни одна дата не появляется дважды для одного и того же пользователя, тогда:
источник
источник