SQL для mobile app аналитики

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

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

Mobile-first компании (VK, Tinder, Duolingo, Mobile-focused Ozon) требуют mobile analytics expertise. Specific metrics: install rate, day-N retention, version adoption, in-app purchases.

На собесах в mobile-heavy companies SQL скил с app-specific queries ожидается.

Ключевые таблицы

-- installs
(user_id, installed_at, platform, app_version, source)

-- sessions
(user_id, session_id, started_at, platform, app_version)

-- events
(user_id, event_name, created_at, properties)

-- iap_transactions
(user_id, product_id, amount, purchased_at)

Installs

SELECT
    DATE(installed_at) AS day,
    platform,
    source,
    COUNT(*) AS installs
FROM installs
WHERE installed_at >= CURRENT_DATE - 30
GROUP BY 1, 2, 3
ORDER BY 1 DESC;

DAU per platform

SELECT
    DATE(started_at) AS day,
    platform,
    COUNT(DISTINCT user_id) AS dau
FROM sessions
WHERE started_at >= CURRENT_DATE - 30
GROUP BY 1, 2;

D1 / D7 / D30 retention

WITH cohort AS (
    SELECT user_id, DATE(installed_at) AS install_date
    FROM installs
    WHERE installed_at >= CURRENT_DATE - 60
),
activity AS (
    SELECT DISTINCT user_id, DATE(started_at) AS active_date
    FROM sessions
)
SELECT
    c.install_date,
    COUNT(DISTINCT c.user_id) AS installs,
    COUNT(DISTINCT CASE WHEN a.active_date = c.install_date + 1 THEN c.user_id END) * 100.0 /
        COUNT(DISTINCT c.user_id) AS d1_retention,
    COUNT(DISTINCT CASE WHEN a.active_date = c.install_date + 7 THEN c.user_id END) * 100.0 /
        COUNT(DISTINCT c.user_id) AS d7_retention,
    COUNT(DISTINCT CASE WHEN a.active_date = c.install_date + 30 THEN c.user_id END) * 100.0 /
        COUNT(DISTINCT c.user_id) AS d30_retention
FROM cohort c
LEFT JOIN activity a ON a.user_id = c.user_id
    AND a.active_date BETWEEN c.install_date AND c.install_date + 30
GROUP BY c.install_date
ORDER BY c.install_date;

Session length

WITH session_durations AS (
    SELECT
        user_id,
        session_id,
        MAX(created_at) - MIN(created_at) AS duration
    FROM events
    GROUP BY user_id, session_id
)
SELECT
    AVG(EXTRACT(EPOCH FROM duration) / 60) AS avg_minutes,
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY duration) AS median_duration
FROM session_durations;

Crash rate

SELECT
    DATE(created_at) AS day,
    platform,
    app_version,
    COUNT(*) AS crashes,
    COUNT(DISTINCT user_id) AS affected_users,
    COUNT(DISTINCT user_id) * 100.0 /
        (SELECT COUNT(DISTINCT user_id) FROM sessions
         WHERE DATE(started_at) = DATE(created_at)) AS crash_rate_pct
FROM events
WHERE event_name = 'app_crash'
GROUP BY 1, 2, 3;

Version adoption

SELECT
    DATE(started_at) AS day,
    app_version,
    COUNT(DISTINCT user_id) AS users,
    COUNT(DISTINCT user_id) * 100.0 / SUM(COUNT(DISTINCT user_id)) OVER (PARTITION BY DATE(started_at)) AS pct
FROM sessions
WHERE started_at >= CURRENT_DATE - 14
GROUP BY 1, 2
ORDER BY 1 DESC, users DESC;

Видно, как новая version gains adoption.

In-app purchase conversion

WITH cohort AS (
    SELECT user_id, DATE(installed_at) AS install_date
    FROM installs
    WHERE installed_at BETWEEN '2026-03-01' AND '2026-03-31'
)
SELECT
    COUNT(DISTINCT c.user_id) AS cohort_size,
    COUNT(DISTINCT iap.user_id) AS paying_users,
    COUNT(DISTINCT iap.user_id) * 100.0 / COUNT(DISTINCT c.user_id) AS paid_conversion_rate
FROM cohort c
LEFT JOIN iap_transactions iap ON iap.user_id = c.user_id
    AND iap.purchased_at <= c.install_date + 30;

ARPDAU

Average Revenue per Daily Active User:

WITH daily AS (
    SELECT
        DATE(started_at) AS day,
        COUNT(DISTINCT user_id) AS dau
    FROM sessions
    GROUP BY 1
),
revenue AS (
    SELECT
        DATE(purchased_at) AS day,
        SUM(amount) AS daily_revenue
    FROM iap_transactions
    GROUP BY 1
)
SELECT
    d.day,
    d.dau,
    COALESCE(r.daily_revenue, 0) AS revenue,
    COALESCE(r.daily_revenue, 0) / d.dau AS arpdau
FROM daily d
LEFT JOIN revenue r USING (day)
ORDER BY d.day;

Funnel: install → first purchase

SELECT
    DATE_TRUNC('month', installed_at) AS cohort,
    COUNT(*) AS installs,
    COUNT(CASE WHEN first_session_at IS NOT NULL THEN 1 END) AS opened,
    COUNT(CASE WHEN tutorial_completed_at IS NOT NULL THEN 1 END) AS completed_tutorial,
    COUNT(CASE WHEN first_purchase_at IS NOT NULL THEN 1 END) AS purchased,
    COUNT(CASE WHEN first_purchase_at IS NOT NULL THEN 1 END) * 100.0 / COUNT(*) AS install_to_paid
FROM user_summary
GROUP BY 1
ORDER BY 1;

Deep-link / referral

SELECT
    source,
    COUNT(*) AS installs,
    AVG(CASE WHEN d7_retained THEN 1 ELSE 0 END) * 100 AS d7_retention,
    AVG(CASE WHEN paid_d30 THEN 1 ELSE 0 END) * 100 AS paid_conversion
FROM installs
WHERE installed_at >= CURRENT_DATE - 60
GROUP BY source
ORDER BY installs DESC;

Notifications effectiveness

WITH push_events AS (
    SELECT
        user_id,
        sent_at,
        MAX(CASE WHEN opened THEN 1 ELSE 0 END) AS opened_push,
        MAX(CASE WHEN event_name = 'session_start'
                 AND created_at BETWEEN sent_at AND sent_at + INTERVAL '10 min'
            THEN 1 ELSE 0 END) AS session_after_push
    FROM push_notifications
    GROUP BY user_id, sent_at
)
SELECT
    AVG(opened_push) * 100 AS open_rate,
    AVG(session_after_push) * 100 AS session_rate
FROM push_events;

Stickiness (DAU/MAU)

WITH monthly AS (
    SELECT COUNT(DISTINCT user_id) AS mau
    FROM sessions
    WHERE started_at BETWEEN CURRENT_DATE - 30 AND CURRENT_DATE
),
daily AS (
    SELECT AVG(daily_count) AS avg_dau
    FROM (
        SELECT DATE(started_at) AS day, COUNT(DISTINCT user_id) AS daily_count
        FROM sessions
        WHERE started_at BETWEEN CURRENT_DATE - 30 AND CURRENT_DATE
        GROUP BY 1
    ) d
)
SELECT d.avg_dau / m.mau AS stickiness FROM daily d, monthly m;

Platform comparison

SELECT
    platform,
    COUNT(DISTINCT user_id) AS users,
    AVG(session_count) AS sessions_per_user,
    AVG(purchase_amount) AS avg_spend,
    AVG(CASE WHEN retained_7d THEN 1 ELSE 0 END) AS retention_d7
FROM user_summary
GROUP BY platform;

iOS vs Android обычно дают different patterns.

На собесе

«Mobile retention — как?»

Install → DAU на day N. Cohort by install date.

«iOS vs Android?»

Retention, ARPU, session length часто differ. Analyze separately.

«Crash analysis?»

Event «app_crash» tracking. Per version / platform / OS version.

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

FAQ

AppMetrica / Firebase — SQL?

Часто data exported в warehouse. SQL на warehouse.

Cross-device?

Hard. User ID linking (если logged in) помогает.

Attribution mobile?

MMP (AppsFlyer, Adjust) обычно. SQL аналитика сверху.


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