SQL для финансового аналитика на собесе
Зачем SQL на собесе финаналитика
В 2026 финансовый аналитик в IT не может обходиться без SQL. Excel + ручной импорт CSV — устарело. Современный финаналитик пишет SQL для retrievals из DWH: revenue по periods, customer breakdown, variance анализ.
На собесе финаналитика SQL — обязательный live-coding раздел. Слабый ответ — «select count». Сильный — window functions, агрегации по периодам, variance анализ.
Базовые операции
SELECT, FROM, WHERE, GROUP BY — стандарт.
JOIN:
- INNER JOIN для transactions + customers
- LEFT JOIN когда нужны customers без orders
- Multiple joins для P&L (revenue + costs + categories)
Aggregations:
SELECT
DATE_TRUNC('month', order_date) AS month,
SUM(amount) AS revenue,
COUNT(*) AS orders,
COUNT(DISTINCT customer_id) AS customers,
AVG(amount) AS avg_check
FROM orders
GROUP BY 1
ORDER BY 1;Period-over-period analysis
Финансовый аналитик постоянно считает MoM / YoY / variance.
LAG для prior period:
SELECT month, revenue,
LAG(revenue) OVER (ORDER BY month) AS prev_revenue,
revenue - LAG(revenue) OVER (ORDER BY month) AS delta,
(revenue / LAG(revenue) OVER (ORDER BY month) - 1) * 100 AS mom_growth_pct
FROM monthly_revenue;YoY (12-month LAG):
LAG(revenue, 12) OVER (ORDER BY month) AS revenue_last_yearRunning totals:
SUM(revenue) OVER (ORDER BY month ROWS UNBOUNDED PRECEDING) AS ytd_revenueMoving averages (smoothing):
AVG(revenue) OVER (ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS revenue_3m_maПодробнее — LAG, LEAD в SQL, оконные функции.
Variance анализ (plan vs actual)
WITH actual AS (
SELECT period, category, SUM(amount) AS actual_amount
FROM transactions
GROUP BY 1, 2
),
budget AS (
SELECT period, category, budget_amount
FROM budget_data
)
SELECT b.period, b.category,
b.budget_amount,
COALESCE(a.actual_amount, 0) AS actual_amount,
COALESCE(a.actual_amount, 0) - b.budget_amount AS variance,
(COALESCE(a.actual_amount, 0) / NULLIF(b.budget_amount, 0) - 1) * 100 AS variance_pct
FROM budget b
LEFT JOIN actual a USING (period, category)
ORDER BY ABS(variance) DESC;P&L SQL
Aggregate revenue + costs по category:
WITH revenue AS (
SELECT DATE_TRUNC('month', DATE) AS month,
SUM(amount) AS total_revenue
FROM transactions WHERE type = 'revenue'
GROUP BY 1
),
costs AS (
SELECT DATE_TRUNC('month', DATE) AS month,
SUM(CASE WHEN category = 'COGS' THEN amount ELSE 0 END) AS cogs,
SUM(CASE WHEN category = 'opex' THEN amount ELSE 0 END) AS opex,
SUM(CASE WHEN category = 'marketing' THEN amount ELSE 0 END) AS marketing
FROM transactions WHERE type = 'expense'
GROUP BY 1
)
SELECT r.month,
r.total_revenue,
c.cogs,
r.total_revenue - c.cogs AS gross_profit,
c.opex,
c.marketing,
r.total_revenue - c.cogs - c.opex - c.marketing AS ebitda
FROM revenue r LEFT JOIN costs c USING (month)
ORDER BY 1;Cohort revenue analysis
Cohort by signup month → revenue в каждый последующий month.
WITH cohorts AS (
SELECT user_id, DATE_TRUNC('month', signup_date) AS cohort_month
FROM users
),
revenue AS (
SELECT user_id, DATE_TRUNC('month', order_date) AS revenue_month,
SUM(amount) AS rev
FROM orders
GROUP BY 1, 2
)
SELECT c.cohort_month, r.revenue_month,
SUM(r.rev) AS cohort_revenue,
COUNT(DISTINCT r.user_id) AS active_users
FROM cohorts c JOIN revenue r USING (user_id)
GROUP BY 1, 2;Categorization / mapping
Часто данные в DWH grain-level, нужно map к финансовым категориям.
CASE / mapping table:
SELECT
t.amount,
CASE
WHEN t.account_code BETWEEN '4000' AND '4999' THEN 'Revenue'
WHEN t.account_code BETWEEN '5000' AND '5999' THEN 'COGS'
WHEN t.account_code BETWEEN '6000' AND '6999' THEN 'OpEx'
ELSE 'Other'
END AS finance_category
FROM transactions t;Лучше: mapping table в database, JOIN с ней.
Типичные вопросы
«MoM growth revenue за последние 12 месяцев»
LAG(revenue) OVER (ORDER BY month). delta + delta_pct.
«YoY variance по region»
Group by region + month. LAG 12 months. Variance = current - last_year.
«Top-10 customers по revenue»
GROUP BY customer + SUM, ORDER BY total DESC LIMIT 10.
«Cash flow прогноз на следующие 3 месяца»
Historical: revenue, expenses by month. Trend extrapolation (LAG / moving avg). Можно отдельно ML — но base в SQL.
«Find users без orders за last 90 days»
LEFT JOIN orders с фильтром last 90d, WHERE order IS NULL.
Частые ошибки
- DATE без timezone. Cross-region отчёты ломаются.
- NULL в aggregations. SUM игнорирует, AVG может surprise.
- GROUP BY без proper aggregation. Logical error.
- Без partition pruning на больших таблицах. Запросы tail.
- Window без partition. Для multi-region — нужен PARTITION BY region.
FAQ
Финаналитика и DWH — какой стек?
ClickHouse / Snowflake / Greenplum / BigQuery в зависимости от компании. SQL диалект ~ANSI базовый.
Excel + SQL вместе?
Yes. SQL для retrieval из DWH, Excel для модели / dashboards. Power Query bridges them.
dbt для финаналитика?
Растёт. dbt-модели для finance mart (P&L, balance sheet, ratios). Standard в 2026.
Python нужен?
Для advanced (forecasting, ML scoring) — да. Для базовой работы — SQL достаточно.
Где практиковать?
SQL-тренажёр, Mode Analytics SQL challenges, open financial datasets.