Как выбрать максимальную цену для каждого артикула при дублирующихся ценах в таблице
Что проверяют
Умеете ли вы отличить агрегат от выбора строки. Если нужна только цена, хватает GROUP BY и MAX, и дубли ему не мешают. Следом почти всегда просят: «А теперь выведите всю строку с максимальной ценой, с поставщиком и датой», и тут появляются оконные функции и вопрос, что делать с ничьими.
Как отвечать
Начните с уточнения: нужна одна цифра на артикул или целая запись. Дальше по случаям:
- только максимум:
SELECT article, MAX(price) FROM prices GROUP BY article. Повторяющиеся значения на результат не влияют,MAXот100, 100, 90равен100, чистить дубли заранее не нужно; - вся строка, одна на артикул:
ROW_NUMBER() OVER (PARTITION BY article ORDER BY price DESC, updated_at DESC)в подзапросе или CTE, снаружиWHERE rn = 1. Второй ключ сортировки решает, какую из равных строк взять, иначе выбор случаен и может меняться от запуска к запуску; - все строки с максимумом:
RANK()илиDENSE_RANK()вместоROW_NUMBER(), тогда при двух одинаковых ценах вернутся обе записи; - PostgreSQL: короткий вариант
SELECT DISTINCT ON (article) * FROM prices ORDER BY article, price DESC; - через соединение: подзапрос с
MAXпо артикулу иJOINобратно по паре артикул и цена. Работает в любой базе, но при ничьей тоже вернёт несколько строк.
Если в таблице есть полные дубли строк, их убирает DISTINCT, но это отдельная задача качества данных, и её стоит назвать вслух: откуда дубли и нужно ли чинить их в источнике.
Где спотыкаются
Добавляют в SELECT колонку, которой нет в GROUP BY, например поставщика. PostgreSQL выдаст ошибку, а MySQL с отключённым ONLY_FULL_GROUP_BY вернёт значение из произвольной строки, не обязательно той, где максимум.
Пишут ROW_NUMBER без второго ключа сортировки и не могут объяснить, почему при повторном запуске для того же артикула пришёл другой поставщик.
Не думают про NULL: MAX пропускает пустые цены, а ORDER BY price DESC в PostgreSQL ставит NULL первыми, поэтому без NULLS LAST строкой номер один окажется запись без цены.