Sql oracle как выбрать строку с максимальным значением

Обновлено: 05.07.2024

У меня есть эта таблица для документов (упрощенная версия здесь):

Как выбрать одну строку на один идентификатор и только наибольший оборот?
С приведенными выше данными результат должен содержать две строки: [1, 3, . ] и [2, 1, ..] . Я использую MySQL.

В настоящее время я использую проверки в цикле while для обнаружения и перезаписи старых revs из набора результатов. Но является ли это единственным методом достижения результата? Разве нет решения SQL?

Обновление
Как показывают ответы, есть SQL-решение, а здесь демо-версия sqlfiddle.

ОТВЕТЫ

Ответ 1

На первый взгляд.

Все, что вам нужно, это предложение GROUP BY с функцией агрегации MAX :

Это так просто, не так ли?

Я просто заметил, что вам нужен столбец content .

Это очень распространенный вопрос в SQL: найдите все данные для строки с некоторым максимальным значением в столбце для каждого идентификатора группы. Я много слышал о своей карьере. Фактически, это был один из вопросов, которые я ответил в своем текущем техническом интервью.

В принципе, у вас есть два подхода к решению этой проблемы:

Соединение с простым group-identifier, max-value-in-group Подзапросом

В этом подходе вы сначала найдете group-identifier, max-value-in-group (уже разрешенный выше) в подзапросе. Затем вы присоединяете свою таблицу к подзапросу с равенством как на group-identifier , так и на max-value-in-group :

Левое соединение с помощью себя, настройка условий соединения и фильтров

В этом подходе вы оставили соединение с самим собой. Равенство, конечно же, идет в group-identifier . Затем два умных перемещения:

  • Второе условие объединения имеет левое боковое значение меньше правого значения
  • Когда вы выполните шаг 1, строки (ы), которые на самом деле имеют максимальное значение, будут иметь NULL в правой части (это a LEFT JOIN , помните?). Затем мы фильтруем объединенный результат, показывая только строки, где правая сторона NULL .

Итак, вы закончите:

Заключение

Оба подхода приносят точный результат.

Если у вас есть две строки с max-value-in-group для group-identifier , обе строки будут в результате в обоих подходах.

Оба подхода совместимы с SQL ANSI, поэтому они будут работать с вашей любимой РСУБД, независимо от ее "вкуса".

Оба подхода также совместимы с производительностью, однако ваш пробег может варьироваться (РСУБД, структура БД, индексы и т.д.). Поэтому, когда вы выбираете один подход по сравнению с другим, ориентир. И убедитесь, что вы выбрали тот, который имеет для вас большой смысл.

Ответ 2

Мое предпочтение заключается в том, чтобы использовать как можно меньше кода.

Вы можете сделать это, используя IN попробуйте следующее:

на мой взгляд, он менее сложный. легче читать и поддерживать.

Ответ 3

Еще одно решение - использовать коррелированный подзапрос:

Наличие индекса (id, rev) делает подзапрос почти как простой поиск.

Ниже приведены сравнения с решениями в ответе @AdrianCarneiro (subquery, leftjoin), основанные на измерениях MySQL с таблицей InnoDB размером

1 миллион записей, размер группы: 1-3.

В то время как для полного сканирования таблицы подзапросы/левые/коррелированные тайминги относятся друг к другу как 6/8/9, когда дело доходит до прямого поиска или партии ( id in (1,2,3) ), подзапрос выполняется намного медленнее, чем остальные (из-за повторной передачи подзапрос). Однако я не мог отличать ледяные и коррелированные решения в скорости.

Наконец, поскольку leftjoin создает n * (n + 1)/2, объединяется в группы, его производительность может сильно зависеть от размера групп.

Ответ 4

Добавлен в стандарт SQL Стандарт ANSI/ISO SQL: 2003 и более поздние версии с ANSI/ISO Standard SQL: 2008, теперь доступны функции окна (или окна) со всеми основными поставщиками. Существует больше типов ранговых функций, доступных для решения проблемы связи: RANK, DENSE_RANK, PERSENT_RANK .

Ответ 5

Я не могу ручаться за производительность, но вот трюк, вдохновленный ограничениями Microsoft Excel. Он имеет некоторые хорошие функции.

GOOD STUFF

  • Он должен принудительно вернуть только одну "максимальную запись", даже если есть галстук (иногда полезный)
  • Он не требует соединения

ПОДХОД

Это немного уродливо и требует, чтобы вы знали что-то о диапазоне допустимых значений столбца rev. Предположим, что мы знаем, что столбец rev - это число от 0,00 до 999, включая десятичные числа, но что только две цифры справа от десятичной точки (например, 34.17 будет действительным значением).

Суть в том, что вы создаете единый синтетический столбец путем конкатенации/упаковки первичного поля сравнения вместе с данными, которые вы хотите. Таким образом, вы можете заставить агрегированную функцию SQL MAX() возвращать все данные (поскольку она была упакована в один столбец). Затем вам нужно распаковать данные.

Вот как он выглядит с приведенным выше примером, написанным на SQL

Упаковка начинается с того, что для того, чтобы столбец rev был числом известных символов, независимо от значения rev, чтобы, например,

  • 3.2 становится 1003.201
  • 57 становится 1057.001
  • 923.88 становится 1923.881

Если вы это сделаете правильно, сравнение строк двух чисел должно давать то же самое "max", что и числовое сравнение двух чисел, и легко преобразовать обратно к исходному номеру с помощью функции подстроки (которая доступна в одной форме или другой почти везде).

Ответ 6

Я думаю, что это самое простое решение:

  • SELECT * : вернуть все поля.
  • FROM Employee : Таблица искалась.
  • (SELECT *. ) подзапрос: Вернуть всех людей, отсортированных по зарплате.
  • GROUP BY employeesub.Salary : Принудительный возврат строки зарплаты каждого сотрудника.

Если вам нужен только один ряд, это еще проще:

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

  • ORDER BY Employee.Salary DESC : ORDER BY Employee.Salary DESC результаты по зарплате, сначала с самой высокой зарплатой.
  • LIMIT 1 : вернуть только один результат.

Ответ 7

Что-то вроде этого?

Ответ 8

Поскольку это самый популярный вопрос, связанный с этой проблемой, я еще раз отвечу на него здесь:

Похоже, есть более простой способ сделать это (но только в MySQL):

Пожалуйста, отметьте ответ пользователя Bohemian в этом вопросе за краткий и элегантный ответ на эту проблему.

Редактировать: хотя это решение работает для многих людей, оно может быть нестабильным в долгосрочной перспективе, поскольку MySQL не гарантирует, что оператор GROUP BY будет возвращать значимые значения для столбцов, не входящих в список GROUP BY. Так что используйте это решение на свой страх и риск!

Ответ 9

Мне нравится использовать решение NOT EXIST EXIST -based для этой проблемы:

Это выберет все записи с максимальным значением в группе и позволит вам выбрать другие столбцы.

Ответ 10

НЕ mySQL, но для других людей, которые находят этот вопрос и используют SQL, другим способом решения проблемы greatest-n-per-group является использование Cross Apply в MS SQL

Вот пример в SqlFiddle

Ответ 11

Третье решение, о котором я почти никогда не упоминал, имеет специфику MySQL и выглядит следующим образом:

Да, это выглядит ужасно (преобразование в строку и обратно и т.д.), но по моему опыту это обычно быстрее, чем другие решения. Возможно, это только для моих случаев использования, но я использовал его на таблицах с миллионами записей и множеством уникальных идентификаторов. Может быть, потому, что MySQL довольно плохо оптимизирует другие решения (по крайней мере, в 5,0 дней, когда я придумал это решение).

Важно то, что GROUP_CONCAT имеет максимальную длину для строки, которую он может создать. Вероятно, вы хотите повысить этот предел, установив переменную group_concat_max_len . И имейте в виду, что это будет ограничение на масштабирование, если у вас есть большое количество строк.

В любом случае вышеупомянутое не работает напрямую, если ваше поле содержимого уже является текстом. В этом случае вы, вероятно, захотите использовать другой разделитель, например, \0. Вы также быстрее столкнетесь с префиксом group_concat_max_len .

Ответ 12

Если у вас много полей в инструкции select, и вы хотите получить последнее значение для всех этих полей с помощью оптимизированного кода:

Ответ 13

Я думаю, ты этого хочешь?

Ответ 14

Как насчет этого:

Ответ 15

Ответ 16

Я бы использовал это:

Подзапрос SELECT не слишком эффективен, но в разделе JOIN кажется полезным. Я не эксперт в оптимизации запросов, но я пробовал в MySQL, PostgreSQL, FireBird и работает очень хорошо.

Вы можете использовать эту схему в нескольких соединениях и с предложением WHERE. Это мой рабочий пример (решение идентично вашей проблеме с таблицей "твердое" ):

Его спрашивают на таблицах с подростками таких записей, и он занимает менее 0,01 секунды на действительно не слишком сильной машине.

Я бы не использовал предложение IN (как упоминается выше). IN предоставляется для использования с короткими списками констант, а не как фильтр запросов, построенный на подзапросе. Это связано с тем, что подзапрос в IN выполняется для каждой отсканированной записи, которая может сделать запрос очень медленным.

Ответ 17

Ответ 18

Другой способ выполнить работу - использовать аналитическую функцию MAX() в предложении OVER PARTITION.

Другое ROW_NUMBER() OVER PARTITION, уже задокументированное в этом посте,

Эти 2 SELECT хорошо работают на Oracle 10g.

Решение MAX(), безусловно, ROW_NUMBER() решение ROW_NUMBER() поскольку сложность MAX() равна O(n) а сложность ROW_NUMBER() - как минимум O(n.log(n)) где n представляет количество записей в таблице!

Ответ 19

Вот хороший способ сделать это

Используйте следующий код:

Ответ 20

Мне нравится делать это путем ранжирования записей в каком-то столбце. В этом случае значения ранга rev сгруппированы по id . Те, у кого выше rev , будут иметь более низкий рейтинг. Таким образом, наивысший rev будет иметь рейтинг 1.

Не уверен, что введение переменных делает все это медленнее. Но, по крайней мере, я не дважды запрашиваю YOURTABLE .

Ответ 21

Отсортировано поле rev в обратном порядке, а затем сгруппировано по id, которое дало первую строку каждой группы, которая является самой высокой величиной rev.

Это дало следующий результат в MySql 5.5 и 5.6

Ответ 22

вот еще одно решение, которое поможет кому-то

Ответ 23

Это то, что сработало для меня.

Ответ 24

Здесь другое решение для получения записей только с полем, которое имеет максимальное значение для этого поля. Это работает для SQL400, на котором я работаю. В этом примере записи с максимальным значением в поле FIELD5 будут получены следующим оператором SQL.

Ответ 25

Если кто-то ищет Linq verson, это, похоже, работает для меня:

Ответ 26

Многие, если не все, другие ответы здесь подходят для небольших наборов данных. Для масштабирования требуется больше внимания. См. здесь.

В нем обсуждается несколько более быстрых способов сделать groupwise max и top-N для каждой группы.

Ответ 27

Я использовал ниже, чтобы решить свою проблему. Сначала я создал временную таблицу и вставил максимальное значение rev на уникальный идентификатор.

Ответ 28

Вы можете сделать выбор без объединения, когда вы объединяете rev и id в одно значение maxRevId для MAX() а затем разделяете его на исходные значения:

Это особенно быстро, когда есть сложное соединение вместо одной таблицы. При традиционных подходах комплексное соединение будет выполнено дважды.

Приведенная выше комбинация проста с битовыми функциями, когда rev и id имеют значение INT UNSIGNED (32 бита), а объединенное значение соответствует значению BIGINT UNSIGNED (64 бита). Когда id & rev больше 32-битных значений или состоят из нескольких столбцов, необходимо объединить значение, например, в двоичное значение с подходящим заполнением для MAX() .

Ответ 29

Это не чистый SQL. Это будет использовать SQLAlchemy ORM.

Я пришел сюда в поисках справки по SQLAlchemy, поэтому я продублирую ответ Адриана Карнейру с версией python/SQLAlchemy, в частности, с внешней частью соединения.

Этот запрос отвечает на вопрос:

"Можете ли вы вернуть мне записи в этой группе записей (на основе одного и того же идентификатора), которые имеют наибольший номер версии".

Это позволяет мне дублировать запись, обновлять ее, увеличивать ее номер версии и получать копию старой версии таким образом, чтобы я мог показывать изменения во времени.

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

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

Что если несколько строк имеют максимальное значение даты для определенного идентификатора пользователя?

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

«Аналитические функции рока»

Редактировать: Что касается первого комментария .

«Использование аналитических запросов и самостоятельное объединение наносят ущерб цели аналитических запросов»

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

«Окно по умолчанию в Oracle - от первой строки в разделе до текущей»

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

Применительно к таблице, имеющей 8,8 миллиона строк, этот запрос занимал половину времени запросов в некоторых других высоко голосуемых ответах. Кто-нибудь хочет опубликовать ссылку на MySQL эквивалент этого, если таковой имеется? Не может ли это вернуть дубликаты? Например. если две строки имеют одинаковый user_id и одну и ту же дату (что является максимальным). Вместо этого 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 .

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

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

Вы, вероятно, недооцениваете оптимизатор запросов Oracle. Не за что. Это почти наверняка будет реализовано как полное сканирование с вложенным циклом, чтобы получить даты. Вы говорите о логических операциях ввода-вывода в 4 раза превышающем количество строк в таблице, и вам страшно за нетривиальные объемы данных. К вашему сведению, «Не эффективно, но работает» - это то же самое, что «Работает, но не эффективно». Когда мы отказались от эффективной цели дизайна? +1, потому что, когда ваши таблицы данных не являются миллионами строк в длину, это самое понятное решение. когда у вас есть несколько разработчиков всех уровней квалификации, которые изменяют код, понятность важнее доли секунды, что незаметно.

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

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

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

Так что это работает, хотя некоторые из новых вещей, упомянутых в другом месте, могут быть более производительными.

Это хорошо работает и на PostgreSQL. И мне нравится его простота и универсальность - подзапрос говорит: «Вот мои критерии», внешний запрос - «И вот детали, которые я хочу увидеть». +1.

Я знаю, что вы просили Oracle, но в SQL 2005 мы теперь используем это:

У меня нет Oracle для его тестирования, но самое эффективное решение - использовать аналитические запросы. Это должно выглядеть примерно так:

Я подозреваю, что вы можете избавиться от внешнего запроса и поставить разные на внутренний, но я не уверен. В то же время я знаю, что это работает.

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

В этом случае вот что делает внутренний запрос. Весь набор данных сортируется по UserId, а затем по дате DESC. Затем он обрабатывает его за один проход. Для каждой строки вы возвращаете UserId и первую Date, увиденную для этого UserId (поскольку даты отсортированы DESC, это максимальная дата). Это дает вам ваш ответ с дублированными строками. Тогда внешний DISTINCT сдавливает дубликаты.

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

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

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

"Аналитические функции рок"

Изменить: Что касается первого комментария.

", используя аналитические запросы и самосоединение, поражает цель аналитических запросов

В этом коде нет самостоятельного объединения. Вместо этого существует предикат, помещенный на результат встроенного представления, который содержит аналитическую функцию - совсем другое дело и полностью стандартную практику.

"Окно по умолчанию в Oracle относится к первой строке раздела к текущему"

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

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

Другими словами: выберите строку из t1, где не существует другой строки с тем же UserId и большей датой.

(я помещаю идентификатор "Дата" в разделители, потому что это зарезервированное слово SQL.)

В случае, если t1."Date" = t2."Date" , появляется удвоение. Обычно таблицы имеют auto_inc(seq) ключ, например. id . Во избежание удвоения можно использовать следующее:

Re comment от @Farhan:

Вот более подробное объяснение:

Внешнее соединение пытается соединить t1 с t2. По умолчанию возвращаются все результаты t1, и если есть совпадение в t2, оно также возвращается. Если в t2 для данной строки t1 нет совпадения, запрос по-прежнему возвращает строку t1 и использует NULL в качестве заполнителя для всех столбцов t2. Это то, как работают внешние соединения.

Трюк в этом запросе заключается в разработке условия согласования соединения, так что t2 должен соответствовать одному и тому же идентификатору пользователя и более высокой дате. Идея заключается в том, что строка существует в t2 с более высокой датой, тогда строка в t1, которую она сравнивает, не может быть наибольшей датой для этого пользователя. Но если совпадения нет - т.е. Если в t2 не существует строки с большей датой, чем строка в t1 - мы знаем, что строка в t1 была строкой с наибольшей датой для данного идентификатора пользователя.

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

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

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

EDIT: просто попробовал это для реального:

Таким образом, это работает, хотя некоторые из новых причудливых вещей, упомянутых в других местах, могут быть более эффективными.

Я знаю, что вы попросили Oracle, но в SQL 2005 мы теперь используем это:

У меня нет Oracle для тестирования, но наиболее эффективным решением является использование аналитических запросов. Он должен выглядеть примерно так:

Я подозреваю, что вы можете избавиться от внешнего запроса и наделить его внутренним, но я не уверен. Тем временем я знаю, что это работает.

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

В этом случае вот что делает внутренний запрос. Весь набор данных сортируется по UserId, а затем DES DES. Затем он обрабатывает его за один проход. Для каждой строки вы возвращаете UserId и первую Date, указанную для этого UserId (поскольку даты сортируются DESC, это максимальная дата). Это дает вам ответ с повторяющимися строками. Затем внешний DISTINCT выдает дубликаты.

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

web программирование, администрирование и всякая всячина, которая может оказаться полезной

Выбираем записи с максимальным значением определенного поля.
На всякий случай - требуется получить не сами максимальные значения, а записи, в которых определенное поле равно максимальному.

Если поля всего два - сработает простой запрос:
id|date
select id, max(date) from table1;

При добавлении еще одного поля задача выбора записей с максимальным значением усложняется:

чтобы получить все записи с максимальной датой:

1) для MySQL версии 4.1 и выше можно сделать одним запросом:
select * from table1 where date in (select max(date) from table1)

2)Если версия ниже, то это надо делать в два запроса:

select @mxdate:=max(date) from table1;
select * from table1 where date=@mxdate;

id|date|count (id, date primary key) - next level

нужно вычислить count для каждого id с последней датой (с максимальным значением даты)

решение в один запрос рабочее, но нерациональное - на склейку и сравнение строк уходит неоправданно много времени
SELECT * FROM cms_catalog_goods_is_arc
WHERE CONCAT( date, id )
IN (SELECT concat( max( date ) , id ) FROM cms_catalog_goods_is_arc GROUP BY id )

Гораздо быстрей операция поиска строк с максимальным значением производится с использованием временных таблиц:

CREATE Temporary TABLE table2 (
`id` int(11) NOT NULL, `date` datetime NOT NULL, `cnt` int(11) NOT NULL,
PRIMARY KEY (`id`,`date`)
) ;
insert into table2 (id,date) select id, max(date) from table1 group by id;
update table2 t2, table1 t1 set t2.cnt=t1.cnt where t2.id=t1.id and t2.date=t1.date;

теперь в table2 все нужные нам записи.

1)
select *
from (
select t.*, row_number()
over(partition by num
order by dt desc) as rn
from <table_name> t
)
where rn = 1;
2)
select t1.*
from <table_name> t1
left join <table_name> t2
on t1.num = t2.num
and t1.dt < t2.dt
where t2.num is null;
3)
select *
from <table_name> t1
where (num, dt) in
(
select num, max(dt)
from <table_name>
group by num
);
4)
select t1.*
from <table_name> t1,
(
select num, max(dt) as dt
from <table_name>
group by num
) v
where t1.num = v.num
and t1.dt = v.dt;
5)
select t1.*
from <table_name> t1
where not exists(
select null
from <table_name> t0
where t1.num = t0.num
and t1.dt < t0.dt
);
6)
select t1.*
from <table_name> t1
where dt = (
select max(dt)
from <table_name> t0
where t1.num = t0.num
)

Метки: mysql

Автор будет признателен, если Вы поделитесь ссылкой на статью, которая Вам помогла:
BB-код (для вставки на форум)

html-код (для вставки в ЖЖ, WP, blogger и на страницы сайта)

ссылка (для отправки по почте)

Читайте также: