Как посчитать NPS в SQL

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

Зачем аналитику считать NPS в SQL

NPS (Net Promoter Score) — главная метрика лояльности в продукте. Простой вопрос «насколько вероятно, что вы порекомендуете нас другу?» даёт сильный сигнал о здоровье бизнеса: высокий NPS ≈ низкий churn и сильный word-of-mouth, низкий NPS — предвестник оттока.

Аналитику регулярно приходят запросы: «какой у нас NPS?», «упал ли он после релиза?», «отличается ли у premium-сегмента от free?». Обычный exporter в BI-инструменте не всегда даёт эту гибкость — нужен SQL, который можно переиспользовать в дашборде, alert, автоматических отчётах.

В этой статье — готовые SQL-запросы, которые можно сразу вставить в Metabase / Superset / dbt-модель:

  • Базовая формула и общий NPS за период
  • Динамика по месяцам
  • Разрез по сегментам (tier, канал, гео)
  • Доверительный интервал
  • Связь NPS с retention

Данные в примерах — таблица nps_responses(user_id, score, responded_at).

Формула

NPS определяется по ответам на шкале 0-10:

  • Promoters: 9-10
  • Passives: 7-8
  • Detractors: 0-6
NPS = % Promoters − % Detractors

Значение от -100 до +100.

1. Общий NPS

WITH counts AS (
    SELECT
        COUNT(*) FILTER (WHERE score BETWEEN 9 AND 10) AS promoters,
        COUNT(*) FILTER (WHERE score BETWEEN 0 AND 6) AS detractors,
        COUNT(*) AS total
    FROM nps_responses
)
SELECT
    (100.0 * promoters / total) - (100.0 * detractors / total) AS nps
FROM counts;

Или компактнее:

SELECT
    100.0 * (
        SUM(CASE WHEN score >= 9 THEN 1 ELSE 0 END) -
        SUM(CASE WHEN score <= 6 THEN 1 ELSE 0 END)
    ) / COUNT(*) AS nps
FROM nps_responses;

2. NPS по месяцам

SELECT
    DATE_TRUNC('month', responded_at) AS month,
    100.0 * (
        SUM(CASE WHEN score >= 9 THEN 1 ELSE 0 END) -
        SUM(CASE WHEN score <= 6 THEN 1 ELSE 0 END)
    ) / COUNT(*) AS nps,
    COUNT(*) AS responses
FROM nps_responses
GROUP BY 1
ORDER BY 1;

3. NPS по сегментам

SELECT
    u.tier,
    100.0 * (
        SUM(CASE WHEN n.score >= 9 THEN 1 ELSE 0 END) -
        SUM(CASE WHEN n.score <= 6 THEN 1 ELSE 0 END)
    ) / COUNT(*) AS nps,
    COUNT(*) AS responses
FROM nps_responses n
JOIN users u ON u.id = n.user_id
GROUP BY u.tier
ORDER BY nps DESC;

4. Доверительный интервал NPS

NPS — это разница двух пропорций. ДИ сложный. Упрощённо (биномиальное approx):

WITH stats AS (
    SELECT
        100.0 * (
            SUM(CASE WHEN score >= 9 THEN 1 ELSE 0 END) -
            SUM(CASE WHEN score <= 6 THEN 1 ELSE 0 END)
        ) / COUNT(*) AS nps,
        COUNT(*) AS n
    FROM nps_responses
    WHERE responded_at >= '2026-04-01'
)
SELECT
    nps,
    nps - 1.96 * 100 * SQRT(0.5 * 0.5 / n) AS ci_lower,
    nps + 1.96 * 100 * SQRT(0.5 * 0.5 / n) AS ci_upper
FROM stats;

Или через bootstrap (в Python).

5. Процент promoters / passives / detractors

SELECT
    100.0 * SUM(CASE WHEN score >= 9 THEN 1 ELSE 0 END) / COUNT(*) AS promoters_pct,
    100.0 * SUM(CASE WHEN score BETWEEN 7 AND 8 THEN 1 ELSE 0 END) / COUNT(*) AS passives_pct,
    100.0 * SUM(CASE WHEN score <= 6 THEN 1 ELSE 0 END) / COUNT(*) AS detractors_pct
FROM nps_responses;

6. Response rate

WITH invites AS (
    SELECT COUNT(*) AS sent FROM nps_invites WHERE sent_at >= '2026-04-01'
),
responses AS (
    SELECT COUNT(*) AS responded FROM nps_responses WHERE responded_at >= '2026-04-01'
)
SELECT
    sent,
    responded,
    100.0 * responded / sent AS response_rate
FROM invites, responses;

7. NPS по cohort

WITH user_cohort AS (
    SELECT id, DATE_TRUNC('month', signup_at) AS cohort
    FROM users
)
SELECT
    uc.cohort,
    100.0 * (
        SUM(CASE WHEN n.score >= 9 THEN 1 ELSE 0 END) -
        SUM(CASE WHEN n.score <= 6 THEN 1 ELSE 0 END)
    ) / COUNT(*) AS nps
FROM nps_responses n
JOIN user_cohort uc ON uc.id = n.user_id
GROUP BY uc.cohort
ORDER BY uc.cohort;

8. Связь NPS с retention

SELECT
    CASE
        WHEN score >= 9 THEN 'promoter'
        WHEN score >= 7 THEN 'passive'
        ELSE 'detractor'
    END AS nps_segment,
    AVG(CASE WHEN u.churned THEN 1.0 ELSE 0 END) AS churn_rate
FROM nps_responses n
JOIN users u ON u.id = n.user_id
GROUP BY 1;

Обычно: detractors churn сильно выше, promoters — ниже.

Частые ошибки

  • Смешивать шкалы (0-10 vs 1-10 vs 0-5)
  • Маленький response rate → sample bias
  • Apple MPP и self-selection в ответах
  • Игнорировать контекст комментариев
  • NPS без динамики (одно число бесполезно)

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

FAQ

NPS шкала 0-10 или 1-10?

Стандарт — 0-10.

Сколько respondents нужно?

Минимум 100-200 для стабильного NPS. Идеально 1000+.

NPS или CSAT?

NPS — долгосрочная лояльность. CSAT — удовлетворённость после конкретного взаимодействия.


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