Почему количество значений в результатах запроса с агрегатными функциями не равно количеству строк
Что проверяют
Понимаете ли вы семантику агрегации: что group by схлопывает строки в группы, а агрегатные функции по-разному относятся к null. Для аналитика и инженера данных это вопрос про доверие к цифрам, потому что расхождение счётчиков почти всегда объясняется одним из этих двух механизмов.
Как отвечать
Разделите два разных расхождения. Первое: строк в результате меньше, потому что каждая группа сворачивается в одну строку. Без group by вся выборка это одна группа и ответ будет из единственной строки, с группировкой строк ровно столько, сколько уникальных комбинаций ключа.
Второе, и обычно спрашивают именно про него: внутри группы число значений не равно числу строк из-за пропусков.
select count(*) as rows_total,
count(discount) as non_null,
count(distinct discount) as uniq,
avg(discount) -- сумма делится на non_null
from orders;count(*) считает строки, count(col) пропускает null, count(distinct col) пропускает ещё и повторы. sum и avg тоже игнорируют null, поэтому знаменатель у среднего это число заполненных значений, а не число строк. Если по смыслу пропуск равен нулю, это надо написать явно через coalesce.
Скажите ещё про фильтры: where отсекает строки до группировки, having отбрасывает готовые группы после неё, и результат у них разный.
Где спотыкаются
Считают, что avg делит на все строки. На колонке с пропусками среднее выйдет выше ожидаемого, и метрика тихо уезжает: ошибка не падает, она просто даёт другое число.
Второе: соединение до агрегации. Связь один ко многим размножает строки левой таблицы, и sum по ней удваивает суммы. Лечится агрегацией в подзапросе или count(distinct id), а не приклеенным сверху distinct, который заодно съест законные дубли.
Третье: пытаются отфильтровать по агрегату в where, получают ошибку, а потом переносят в having любое условие подряд. Условие по обычной колонке в having работает, но считается на большем объёме и читается хуже, его место в where.