Как посчитать перцентили в SQL

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Зачем это знать

Среднее время загрузки страницы 200ms — звучит ок, пока не увидишь, что P95 = 3 секунды. Среднее скрывает хвосты, перцентили — показывают. Любой SRE-dashboard и любой performance-отчёт содержат P50, P90, P95, P99.

На собесах: «рассчитай P95 response_time по серверам» — классическая middle-задача.

Что такое перцентиль

P90 = значение, ниже которого лежит 90% данных. P95 — 95%, P99 — 99% (tail).

P50 = медиана.

PERCENTILE_CONT (Postgres, MSSQL)

SELECT
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY response_ms) AS p50,
    PERCENTILE_CONT(0.9) WITHIN GROUP (ORDER BY response_ms) AS p90,
    PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY response_ms) AS p95,
    PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY response_ms) AS p99
FROM requests;

Одним запросом. _cont интерполирует между соседними значениями.

ClickHouse

SELECT
    quantile(0.5)(response_ms) AS p50,
    quantile(0.9)(response_ms) AS p90,
    quantile(0.95)(response_ms) AS p95,
    quantile(0.99)(response_ms) AS p99
FROM requests;

Быстро, approximate (но точно для практики).

Exact вариант:

SELECT quantileExact(0.95)(response_ms) FROM requests;

По группам

Perсентили по endpoint:

SELECT endpoint,
       PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY response_ms) AS p95
FROM requests
GROUP BY endpoint
ORDER BY p95 DESC;

Видны endpoints с worst latency.

MySQL (до 8.0)

Через ROW_NUMBER или window functions в 8.0+:

WITH ranked AS (
    SELECT response_ms,
           ROW_NUMBER() OVER (ORDER BY response_ms) AS rn,
           COUNT(*) OVER () AS n
    FROM requests
)
SELECT response_ms AS p95
FROM ranked
WHERE rn = CEIL(n * 0.95);

PERCENTILE_DISC

Возвращает существующее значение (не интерполирует):

PERCENTILE_DISC(0.95) WITHIN GROUP (ORDER BY response_ms)

На практике почти эквивалентно _cont на больших данных.

Не-стандартные перцентили

P99.9 (тяжёлый tail):

PERCENTILE_CONT(0.999) WITHIN GROUP (ORDER BY response_ms)

Нужно много данных (иначе один outlier = P99.9).

HDR Histograms

Для реального high-scale логирования latency используют HDR-гистограммы, не SQL. ClickHouse quantileTDigest — approximate, но быстро.

На собесе

«Как посчитать P95 response time по endpoint?».

Идеальный ответ: PERCENTILE_CONT + GROUP BY endpoint. Упомянуть quantile в ClickHouse для fast approximate.

Связанные темы

FAQ

Почему не среднее?

Среднее скрывает outliers. Перцентили показывают распределение.

Разница quantile и quantileExact?

Exact точный, но медленный. Approximate — быстрый, точность достаточна для мониторинга.

Как визуализировать?

Box plot или series P50/P90/P95/P99 — стандарт.


Тренируйте SQL — откройте тренажёр с 1500+ вопросами для собесов.