Как посчитать медиану в SQL
Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.
Зачем это знать
Средний чек и средняя зарплата — показатели с тяжёлыми хвостами, где среднее врёт. Медиана устойчивее к выбросам, потому что делит выборку пополам по количеству. Отсюда частый вопрос на собесах: «как посчитать медиану в SQL».
Задача нетривиальная — в большинстве диалектов нет MEDIAN() как aggregate. Надо знать 2-3 способа в зависимости от движка.
Способ 1: PERCENTILE_CONT (Postgres, MSSQL, BigQuery)
Самый простой:
SELECT PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median
FROM employees;PERCENTILE_CONT(0.5) = медиана. Интерполирует между двумя средними значениями при чётном N.
Способ 2: PERCENTILE_DISC
SELECT PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY salary) AS median
FROM employees;Отличие: возвращает существующее значение, не интерполирует. Часто эквивалентно на больших данных.
Способ 3: ROW_NUMBER (универсальный)
Работает везде:
WITH ordered AS (
SELECT salary,
ROW_NUMBER() OVER (ORDER BY salary) AS rn,
COUNT(*) OVER () AS n
FROM employees
)
SELECT AVG(salary) AS median
FROM ordered
WHERE rn IN ((n+1)/2, (n+2)/2);Для чётного N берёт среднее двух серединных. Для нечётного — одну строку.
Способ 4: NTILE
Поделить на 2 группы:
WITH halves AS (
SELECT salary, NTILE(2) OVER (ORDER BY salary) AS half
FROM employees
)
SELECT MAX(CASE WHEN half = 1 THEN salary END) AS median
FROM halves;Приблизительный способ. Для exact — используйте ROW_NUMBER.
ClickHouse
SELECT quantile(0.5)(salary) FROM employees;Или median():
SELECT median(salary) FROM employees;Быстро, специально оптимизировано.
MySQL
До 8.0 нет window functions → через variables:
SELECT AVG(salary) AS median FROM (
SELECT salary, @rn := @rn + 1 AS rn, @total := (SELECT COUNT(*) FROM employees) AS n
FROM employees, (SELECT @rn := 0) r
ORDER BY salary
) t
WHERE rn IN (FLOOR((n+1)/2), CEIL((n+1)/2));В 8.0+ — ROW_NUMBER как в Postgres.
Медиана по группам
Медиана зарплаты по department:
SELECT department,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY salary) AS median
FROM employees
GROUP BY department;В одну строку.
На собесе
Задача: «Посчитай медиану зарплаты в таблице employees».
Junior: «В Postgres — PERCENTILE_CONT». Middle: «А если MySQL до 8? Через ROW_NUMBER и FLOOR/CEIL от (N+1)/2». Senior: «Для approximate — можно NTILE или quantile в ClickHouse, trade-off между точностью и скоростью».
Связанные темы
FAQ
MEDIAN() есть где-то?
Oracle, Snowflake — да. Большинство движков — нет.
Почему не использовать AVG?
AVG — среднее, не медиана. На данных с outliers даёт разный результат.
Percentile_cont или percentile_disc?
_Cont интерполирует (точнее на малых данных). _Disc возвращает существующее значение.
Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.