Как посчитать DAU/MAU в SQL

Закрепи формулу dau mau в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать dau mau в Telegram

Зачем DAU/MAU

DAU/MAU (stickiness) — habit metric. % MAU returning daily. Facebook 50%+, Instagram 50%+, Snapchat 50%+. SaaS типично 20-30%. Below 10% — weak habit.

Формула

DAU/MAU = DAU / MAU × 100%

DAU и MAU за same day (DAU = today, MAU = last 30 days including today).

Базовый расчёт

WITH dau AS (
    SELECT
        DATE,
        COUNT(DISTINCT user_id) AS dau
    FROM activity
    WHERE DATE >= CURRENT_DATE - INTERVAL '60 days'
    GROUP BY DATE
),
mau AS (
    SELECT
        DATE,
        COUNT(DISTINCT user_id) AS mau
    FROM (
        SELECT
            d.DATE,
            a.user_id
        FROM (
            SELECT DISTINCT DATE
            FROM activity
            WHERE DATE >= CURRENT_DATE - INTERVAL '30 days'
        ) d
        JOIN activity a
          ON a.DATE BETWEEN d.DATE - INTERVAL '29 days' AND d.DATE
    ) AS x
    GROUP BY DATE
)
SELECT
    d.DATE,
    d.dau,
    m.mau,
    d.dau::NUMERIC * 100 / NULLIF(m.mau, 0) AS dau_mau_pct
FROM dau d
JOIN mau m ON m.DATE = d.DATE
ORDER BY d.DATE;

Простой alternative (если activity огромный):

SELECT
    DATE_TRUNC('day', activity_date) AS day,
    COUNT(DISTINCT user_id) FILTER (WHERE activity_date = DATE_TRUNC('day', activity_date)) AS dau,
    COUNT(DISTINCT user_id) AS mau_window,
    -- DAU / MAU
    COUNT(DISTINCT user_id) FILTER (WHERE activity_date = DATE_TRUNC('day', activity_date))::NUMERIC * 100
    / NULLIF(COUNT(DISTINCT user_id), 0) AS stickiness
FROM activity
WHERE activity_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY 1
ORDER BY 1 DESC
LIMIT 1;

По сегментам

SELECT
    u.country,
    COUNT(DISTINCT a.user_id) FILTER (WHERE a.DATE = CURRENT_DATE - INTERVAL '1 day') AS dau,
    COUNT(DISTINCT a.user_id) AS mau,
    COUNT(DISTINCT a.user_id) FILTER (WHERE a.DATE = CURRENT_DATE - INTERVAL '1 day')::NUMERIC * 100
    / NULLIF(COUNT(DISTINCT a.user_id), 0) AS stickiness
FROM activity a
JOIN users u ON u.user_id = a.user_id
WHERE a.DATE >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY u.country
ORDER BY stickiness DESC;
Закрепи формулу dau mau в Карьернике
Запомнить надолго — 5 коротких сессий с задачами на эту тему. Бесплатно
Тренировать dau mau в Telegram

Тренд

WITH daily AS (
    SELECT
        a.DATE,
        COUNT(DISTINCT a.user_id) AS dau,
        (SELECT COUNT(DISTINCT user_id)
         FROM activity
         WHERE DATE BETWEEN a.DATE - INTERVAL '29 days' AND a.DATE) AS mau
    FROM activity a
    WHERE a.DATE >= CURRENT_DATE - INTERVAL '90 days'
    GROUP BY a.DATE
)
SELECT
    DATE,
    dau,
    mau,
    dau::NUMERIC * 100 / NULLIF(mau, 0) AS stickiness,
    -- 7-day MA для stability
    AVG(dau::NUMERIC * 100 / NULLIF(mau, 0)) OVER (
        ORDER BY DATE ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS stickiness_ma7
FROM daily
ORDER BY DATE;

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

Ошибка 1. MAU = trailing 30 days. Not «MAU at end of month». Use rolling 30 days for daily computation.

Ошибка 2. Confused with WAU/MAU. WAU/MAU другая метрика (weak habit threshold).

Ошибка 3. New users скew. First-day signups inflate DAU. Cohort-based stickiness more honest.

Ошибка 4. Bot / spam traffic. Filter to real users. Bots crank DAU but не habit.

Ошибка 5. Multi-device. Same user, multiple devices → multiple user_ids? ID resolution.

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

FAQ

Какой DAU/MAU ok?

Social: 50%+. Messaging: 60%+. SaaS: 20-30%. Below 10% — habit weak.

DAU/MAU vs WAU/MAU?

DAU/MAU strict daily habit. WAU/MAU — weekly habit (more relaxed).

Stickiness падает — что значит?

Either DAU down (engagement drop) или MAU up too fast (новые users not retaining).

Why 30 days for MAU?

Convention. Could be 28 days (4 weeks). 30 — calendar match.

DAU/MAU per segment?

Yes! Segment by acquisition channel, country, tenure. Diff stickiness per segment.