Как группировать по заказу DESC
У меня есть следующая таблица, называемая вопросами:
ID | asker
1 | Bob
2 | Bob
3 | Marley
Я хочу, чтобы каждый выборчик выбирал только один раз, и если есть несколько запросов с тем же именем, выберите один из самых высоких идентификаторов. Итак, ожидаемые результаты:
ID | asker
3 | Marley
2 | Bob
Я использую следующий запрос:
SELECT * FROM questions GROUP by questions.asker ORDER by questions.id DESC
Получаю следующий результат:
ID | asker
3 | Marley
1 | Bob
Таким образом, он выбирает первый "Боб", с которым он сталкивается, вместо последнего.
Спасибо
Ответы
Ответ 1
Если вам нужен последний id
для каждого asker
, вам следует использовать функцию агрегирования:
SELECT max(id) as id,
asker
FROM questions
GROUP by asker
ORDER by id DESC
Причина, по которой вы получили необычный результат, заключается в том, что MySQL использует расширение GROUP BY
которое позволяет элементам в списке выбора быть не агрегированными и не включенными в предложение GROUP BY. Это, однако, может привести к неожиданным результатам, потому что MySQL может выбирать возвращаемые значения. (См. Расширения MySQL для GROUP BY)
Из документов MySQL:
MySQL расширяет использование GROUP BY, так что список выбора может ссылаться на неагрегированные столбцы, не указанные в предложении GROUP BY.... Вы можете использовать эту функцию для повышения производительности, избегая ненужной сортировки и группировки столбцов. Однако это полезно, прежде всего, когда все значения в каждом неагрегированном столбце, не названном в GROUP BY, одинаковы для каждой группы. Сервер может свободно выбирать любое значение из каждой группы, поэтому, если они не совпадают, выбранные значения являются неопределенными. Кроме того, на выбор значений из каждой группы нельзя повлиять, добавив предложение ORDER BY. Сортировка набора результатов происходит после выбора значений, и ORDER BY не влияет на то, какие значения выбирает сервер.
Теперь, если у вас есть другие столбцы, которые нужно вернуть из таблицы, но вы не хотите добавлять их в GROUP BY
из-за противоречивых результатов, которые вы можете получить, то вы можете использовать для этого подзапрос. (Демо)
select
q.Id,
q.asker,
q.other -- add other columns here
from questions q
inner join
(
-- get your values from the group by
SELECT max(id) as id,
asker
FROM questions
GROUP by asker
) m
on q.id = m.id
order by q.id desc
Ответ 2
Обычно MySQL позволяет группировать только записи по возрастанию. Поэтому мы можем заказать записи перед группировкой.
SELECT *
FROM (
SELECT *
FROM questions
ORDER BY id DESC
) AS questions
GROUP BY questions.asker
Ответ 3
Записи должны быть сгруппированы с помощью GROUP BY
и MAX()
, чтобы получить максимальный идентификатор для каждого asker
.
SELECT asker, MAX(ID) ID
FROM TableName
GROUP BY asker
ВЫХОД
╔════════╦════╗
║ ASKER ║ ID ║
╠════════╬════╣
║ Bob ║ 2 ║
║ Marley ║ 3 ║
╚════════╩════╝
Ответ 4
Другие верны в использовании MAX (ID) для получения желаемых результатов. Если вам интересно, почему ваш запрос не работает, потому что ORDER BY
происходит после GROUP BY
.
Ответ 5
Чтобы получить каждый столбец:
SELECT * FROM questions
WHERE id IN
(SELECT max(id) as id, asker
FROM questions
GROUP by asker
ORDER by id DESC)
Улучшена версия ответа @bluefeet.
Ответ 6
Это потому, что ORDER BY
выполняется ПОСЛЕ GROUP BY
.
Попробуй это:
SELECT * FROM questions
WHERE id IN
(
SELECT max(id) as id
FROM questions
GROUP by asker
ORDER by id DESC
)
Ответ 7
Я пишу этот ответ, потому что первая альтернатива @Taryn в принятом ответе работает, только если вы точно выбираете только столбцы, используемые в GROUP BY и MAX. Пользователь задает вопрос, выбирая все столбцы в таблице (он использовал SELECT *). Поэтому, когда вы добавляете в таблицу еще 3-й столбец, значение этого столбца в результате запроса будет неверным. Вы получите смешанные значения из разных строк таблицы. Вторая/более длинная альтернатива @Taryn (с использованием внутреннего соединения и подзапроса) работает, но запрос бесполезно сложен и в моем случае использования в 5 раз медленнее, чем моя простая альтернатива ниже.
Рассмотрим questions
таблицы:
id | asker
-----------
1 | Bob
2 | Bob
3 | Marley
Запрос SELECT max(id) as id, asker FROM questions GROUP BY asker ORDER BY id DESC
возвращает ожидаемое:
id | asker
-----------
3 | Marley
2 | Bob
Теперь рассмотрим еще одну таблицу questions
:
id | asker | other
-------------------
1 | Bob | 1st
2 | Bob | 2nd
3 | Marley | 3rd
Запрос SELECT max(id) as id, asker, other FROM questions GROUP BY asker ORDER BY id DESC
возвращает неожиданное:
id | asker | other
-------------------
3 | Marley | 3rd
2 | Bob | 1st
... обратите внимание, что значение other
для второй строки результата неверно, поскольку id=2
поступает из второй строки таблицы, а other=1st
- из первой строки таблицы! Таким образом, многие пользователи в комментариях Тарына сообщают, что это решение не работает.
Возможное простое решение при выборе еще одного столбца - использовать GROUP BY
+ DESC
:
SELECT id, asker, other FROM questions GROUP BY asker DESC
id | asker | other
-------------------
3 | Marley | 3rd
2 | Bob | 2nd
(см. демонстрацию: https://www.db-fiddle.com/f/esww483qFQXbXzJmkHZ8VT/10)
... но это простое решение имеет некоторые ограничения:
- Таблица должна быть InnoDB (я думаю, что это не проблема, потому что вы получите лучшую производительность, а также, поскольку MySQL> = 5.5.5 по умолчанию/предпочтительный механизм хранения был изменен с MyISAM на InnoDB)
- Вы должны создать индекс для столбца, который используется в GROUP BY - поэтому
asker
в этом случае (я думаю, что это не проблема, потому что вы получите лучшую производительность, так как индекс подходит в этом случае. GROUP BY обычно требует создания таблицы tmp, но когда индекс доступен tmp таблица не будет создана, что быстрее) - Для MySQL 5.7 и 8.0 необходимо отключить режим SQL ONLY_FULL_GROUP_BY (например,
SET SESSION sql_mode = '';
) или использовать ANY_VALUE()
для выбранных столбцов, которые не агрегированы, чтобы избежать ошибки ER_WRONG_FIELD_WITH_GROUP. - К сожалению, разработчики MySQL прекратили поддержку ASC/DESC с помощью GROUP BY, начиная с MySQL 8.0 https://dev.mysql.com/worklog/task/?id=8693, но, к счастью, есть альтернатива
GROUP BY col1 ORDER BY col1 ASC/DESC
:
SELECT id, asker, other FROM questions GROUP BY asker ORDER BY asker DESC
id | asker | other
-------------------
3 | Marley | 3rd
2 | Bob | 2nd
(см. демонстрацию: https://www.db-fiddle.com/f/esww483qFQXbXzJmkHZ8VT/11)
... результат такой же, как и выше для GROUP BY... DESC
(не забудьте использовать InnoDB и создать индекс).