Могу ли я использовать неагрегированные столбцы с группировкой по? - PullRequest
16 голосов
/ 02 июля 2010

Вы не можете (не должны) помещать неагрегаты в строку SELECT запроса GROUP BY.

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

CREATE TABLE stuff (
   id int,
   kind int,
   age int
);

Этот запрос дает мне информацию, которую я ищу:

SELECT kind, MAX(age)
FROM stuff
GROUP BY kind;

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

Я ищу что-то вроде этого:

SELECT id, kind, MAX(age)
FROM stuff
GROUP BY kind;

Это выводит это:

SELECT stuff.*
FROM
   stuff,
   ( SELECT kind, MAX(age)
     FROM stuff
     GROUP BY kind) maxes
WHERE
   stuff.kind = maxes.kind AND
   stuff.age = maxes.age

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

Ответы [ 6 ]

12 голосов
/ 03 июля 2010

Невозможно получить идентификатор строки, найденной MAX, поскольку может быть не один идентификатор с максимальным возрастом.

6 голосов
/ 02 июля 2010

Вы не можете (не должны) помещать неагрегаты в строку SELECT запроса GROUP BY.

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

MySQL (и SQLite) в своей бесконечной мудрости решили, что они пойдут против спецификации, и позволят запросам принимать предложения GROUP BY без пропущенных столбцов в SELECT - это фактически делает эти запросы не переносимыми.

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

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

2 голосов
/ 12 мая 2013

В последних базах данных вы можете использовать sum () over (parition by ...) для решения этой проблемы:

select id, kind, age as max_age from (
  select id, kind, age, max(age) over (partition by kind) as mage
    from table)
where age = mage

Это может быть один проход

2 голосов
/ 02 июля 2010

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

SELECT MAX( CONCAT( LPAD(age, 10, '0'), '-', id)
FROM STUFF1
GROUP BY kind;

Если сработает, вам придется разделить ответ, чтобы получить возраст и идентификатор.(Хотя это действительно некрасиво)

1 голос
/ 02 июля 2010

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

Иными словами, как бы вы ожидали, что запрос будет вести себя, если вы замените max на sum?

AnВнутреннее объединение может быть более эффективным, чем ваш подзапрос.

0 голосов
/ 27 июня 2018

Здесь будет полезно DISTINCT ON для PostgesSQL.

SELECT DISTINCT ON (kind) kind, id, age 
FROM stuff
ORDER BY kind, age DESC;

Группирует по виду и возвращает первую строку в упорядоченном формате. Как мы упорядочили по возрасту в порядке убывания, мы получим строку с максимальным возрастом для вида.

P.S. столбцы в DISTINCT ON должны появляться в первом порядке по

...