Проблема
Предположим, что у меня есть эта таблица tab
(fiddle).
| g | a | b | v |
---------------------
| 1 | 3 | 5 | foo |
| 1 | 4 | 7 | bar |
| 1 | 2 | 9 | baz |
| 2 | 1 | 1 | dog |
| 2 | 5 | 2 | cat |
| 2 | 5 | 3 | horse |
| 2 | 3 | 8 | pig |
Я группирую строки по g
, и для каждой группы я хочу одно значение из столбца v
. Тем не менее, я не хочу никакой ценности, но мне нужно значение из строки с максимальным a
и из всех тех, которые имеют максимальный b
. Другими словами, мой результат должен быть
| 1 | bar |
| 2 | horse |
Текущее решение
Я знаю запрос для достижения этого:
SELECT grps.g,
(SELECT v FROM tab
WHERE g = grps.g
ORDER BY a DESC, b DESC
LIMIT 1) AS r
FROM (SELECT DISTINCT g FROM tab) grps
Вопрос
Но я считаю этот запрос скорее уродливым. В основном потому, что он использует зависимый подзапрос , который похож на реального убийцы производительности. Поэтому я задаюсь вопросом, есть ли более легкое решение этой проблемы.
Ожидаемые ответы
Самый вероятный ответ, который я ожидаю в этом вопросе, будет своего рода дополнением или патчем для MySQL (или MariaDB), который предоставляет для этого функцию. Но я также приветствую другие полезные вдохновения. Все, что работает без зависимого подзапроса, будет квалифицироваться как ответ.
Если ваше решение работает только для одного столбца заказа, то есть не может различать cat
и horse
, не стесняйтесь предлагать этот ответ, а также я ожидаю, что он будет по-прежнему полезен для большинства случаев использования, Например, 100*a+b
был бы вероятным способом упорядочить вышеуказанные данные обоими столбцами, все еще используя только одно выражение.
У меня есть несколько довольно хакерских решений, и я мог бы добавить их через некоторое время, но сначала я посмотрю и посмотрю, сначала ли в них появляются новые новые.
Результаты тестов
Как трудно сравнивать различные ответы, просто взглянув на них, я запустил некоторые тесты. Это было выполнено на моем собственном рабочем столе, используя MySQL 5.1. Числа не будут сравниваться ни с одной другой системой, только друг с другом. Вы, вероятно, должны делать свои собственные тесты с вашими реальными данными, если производительность имеет решающее значение для вашего приложения. Когда появятся новые ответы, я могу добавить их в мой script и повторно запустить все тесты.
- 100 000 наименований, 1000 групп на выбор, InnoDb:
- 0.166s для MvG (из вопроса)
- 0.520s для RichardTheKiwi
- 2.199s для xdazz
- 19.24s для Dems (последовательные подзапросы)
- 48.72s для acatt
- 100 000 наименований, 50 000 групп на выбор, InnoDb:
- 0.356s для xdazz
- 0.640s для RichardTheKiwi
- 0.764s для MvG (из вопроса)
- 51.50s для acatt
- слишком долго для Dems (последовательные подзапросы)
- 100 000 наименований, 100 групп на выбор, InnoDb:
- 0.163s для MvG (из вопроса)
- 0.523s для RichardTheKiwi
- 2.072s для Dems (последовательные подзапросы)
- 17.78s для xdazz
- 49.85s для acatt
Итак, кажется, что мое собственное решение пока не так уж плохо, даже с зависимым подзапросом. Удивительно, но решение acatt, которое также использует зависимый подзапрос и который я бы рассмотрел примерно так же, намного хуже. Вероятно, оптимизатор MySQL не может справиться. Решение, предложенное RichardTheKiwi, похоже, имеет хорошую общую производительность. Два других решения в значительной степени зависят от структуры данных. Со многими группами небольших групп подход xdazz превосходит все остальные, тогда как решение Dems лучше всего работает (хотя и не очень хорошо) для нескольких больших групп.