SQL vs Pandas — когда что использовать

Коротко

SQL работает с данными в базе. Pandas — в памяти Python. Оба решают одни задачи: фильтрация, агрегация, соединение таблиц. Аналитику нужны оба: SQL — для выгрузки и предобработки на стороне БД, pandas — для дальнейшего анализа, визуализации и ad hoc задач. На собеседованиях часто просят решить одну задачу и в SQL, и в pandas.

Таблица соответствий

Операция SQL Pandas
Выбор столбцов SELECT col1, col2 df[['col1', 'col2']]
Все столбцы SELECT * df
Фильтрация WHERE condition df[df['col'] > 10]
Сортировка ORDER BY col DESC df.sort_values('col', ascending=False)
Лимит LIMIT 10 df.head(10)
Уникальные SELECT DISTINCT col df['col'].unique()
Группировка GROUP BY col df.groupby('col')
Агрегация SUM(col), COUNT(*) .sum(), .count()
Соединение JOIN ... ON df.merge(df2, on='key')
Подсчёт уникальных COUNT(DISTINCT col) df['col'].nunique()
Новый столбец SELECT col * 2 AS new df['new'] = df['col'] * 2
Пропуски WHERE col IS NOT NULL df[df['col'].notna()]
Замена COALESCE(col, 0) df['col'].fillna(0)
Оконные функции OVER (PARTITION BY ...) df.groupby('col').transform(...)

Параллельные примеры

SELECT + WHERE + ORDER BY

-- SQL
SELECT user_id, amount, order_date
FROM orders
WHERE amount > 1000
ORDER BY amount DESC
LIMIT 10;
# Pandas
(df[df['amount'] > 1000]
    [['user_id', 'amount', 'order_date']]
    .sort_values('amount', ascending=False)
    .head(10))

GROUP BY + агрегация

-- SQL
SELECT
    user_id,
    COUNT(*) AS order_count,
    SUM(amount) AS total,
    AVG(amount) AS avg_check
FROM orders
GROUP BY user_id
HAVING COUNT(*) >= 5
ORDER BY total DESC;
# Pandas
(df.groupby('user_id')
    .agg(
        order_count=('amount', 'count'),
        total=('amount', 'sum'),
        avg_check=('amount', 'mean')
    )
    .query('order_count >= 5')
    .sort_values('total', ascending=False))

В SQL HAVING фильтрует после группировки. В pandas — .query() или булева фильтрация после .agg().

JOIN

-- SQL
SELECT
    o.order_id,
    o.amount,
    u.name,
    u.city
FROM orders o
LEFT JOIN users u ON o.user_id = u.user_id;
# Pandas
orders.merge(users, on='user_id', how='left')[
    ['order_id', 'amount', 'name', 'city']
]

Подробнее: JOIN в SQL, merge в pandas, concat vs merge.

Оконные функции

-- SQL: нарастающая сумма по пользователю
SELECT
    user_id,
    order_date,
    amount,
    SUM(amount) OVER (
        PARTITION BY user_id ORDER BY order_date
    ) AS cumulative_total
FROM orders;
# Pandas
df['cumulative_total'] = (
    df.sort_values('order_date')
    .groupby('user_id')['amount']
    .cumsum()
)
-- SQL: ранг внутри группы
SELECT
    user_id,
    amount,
    ROW_NUMBER() OVER (
        PARTITION BY user_id ORDER BY amount DESC
    ) AS rn
FROM orders;
# Pandas
df['rn'] = (
    df.groupby('user_id')['amount']
    .rank(ascending=False, method='first')
    .astype(int)
)

Подробнее: оконные функции SQL, LAG/LEAD.

CASE WHEN

-- SQL
SELECT
    user_id,
    CASE
        WHEN total_revenue >= 50000 THEN 'premium'
        WHEN total_revenue >= 10000 THEN 'regular'
        ELSE 'new'
    END AS segment
FROM user_stats;
# Pandas
import numpy as np

df['segment'] = np.select(
    [df['total_revenue'] >= 50000, df['total_revenue'] >= 10000],
    ['premium', 'regular'],
    default='new'
)

# Или через apply (медленнее)
df['segment'] = df['total_revenue'].apply(
    lambda x: 'premium' if x >= 50000 else 'regular' if x >= 10000 else 'new'
)

Подзапросы / CTE

-- SQL: CTE
WITH user_stats AS (
    SELECT user_id, SUM(amount) AS total
    FROM orders
    GROUP BY user_id
)
SELECT u.name, s.total
FROM users u
JOIN user_stats s ON u.user_id = s.user_id
WHERE s.total > 100000;
# Pandas: промежуточный DataFrame = CTE
user_stats = (
    orders.groupby('user_id')['amount']
    .sum()
    .reset_index(name='total')
)
result = users.merge(user_stats, on='user_id').query('total > 100000')

В pandas промежуточные DataFrame — это аналог CTE.

Когда что использовать

SQL лучше, когда

  • Данные в БД — не нужно выгружать всё в память
  • Большие объёмы — БД оптимизирует выполнение (индексы, параллелизм)
  • Регулярные отчёты — запрос можно сохранить, зашедулить, поделиться
  • JOIN нескольких таблиц — БД оптимизирует порядок JOIN
  • Витрины данных — результат запроса → новая таблица

Pandas лучше, когда

  • Ad hoc анализ — быстрый EDA, ipynb-ноутбук
  • Данные из разных источников — CSV, API, Excel, JSON
  • Визуализацияmatplotlib, seaborn, plotly
  • Трансформации — сложные вычисления, apply, lambda
  • ML-пайплайн — pandas → scikit-learn → модель
  • Данные в памяти — файлы, не БД

Типичный workflow аналитика

SQL: выгрузка данных из БД
  ↓
Pandas: обработка, анализ, feature engineering
  ↓
matplotlib/seaborn: визуализация
  ↓
Jupyter notebook / дашборд: результат
import pandas as pd
from sqlalchemy import create_engine

engine = create_engine('postgresql://...')

# SQL → Pandas
df = pd.read_sql('''
    SELECT user_id, order_date, amount
    FROM orders
    WHERE order_date >= '2025-01-01'
''', engine)

# Дальнейший анализ в pandas
monthly = df.groupby(df['order_date'].dt.to_period('M'))['amount'].sum()
monthly.plot(kind='bar')

Производительность

Критерий SQL Pandas
Объём данных Миллиарды строк Миллионы строк
Ограничение Диск + RAM сервера RAM ноутбука
Индексы Да, ускоряют запросы Нет (или примитивные)
Параллелизм Встроенный Нет (без Dask/Polars)
Latency Сетевая задержка Мгновенный доступ

Для 10 млн строк SQL и pandas работают сопоставимо. Для 100+ млн — SQL (или ClickHouse, BigQuery). Для 1000 строк — pandas быстрее (нет сетевой задержки).

Типичные ошибки

Выгружать всю таблицу в pandas. pd.read_sql('SELECT * FROM events') для таблицы в 500 млн строк — OutOfMemoryError. Агрегируйте и фильтруйте в SQL, в pandas забирайте результат.

Писать SQL-логику в pandas. Если данные уже в БД — фильтрация, JOIN и GROUP BY эффективнее в SQL. Не нужно тянуть сырые данные в pandas для простой агрегации.

Игнорировать один из инструментов. «Мне хватает SQL» или «Мне хватает pandas» — ограничивает возможности. На собеседованиях ожидают владение обоими.

Вопросы с собеседований

-- Чем SQL отличается от pandas? -- SQL работает с данными в БД (серверная обработка). Pandas — в памяти Python (локальная обработка). SQL лучше для больших объёмов и регулярных запросов. Pandas — для ad hoc анализа, визуализации, ML.

-- Аналог GROUP BY HAVING в pandas? -- df.groupby('col').agg(...).query('count >= 5') или фильтрация по результату агрегации. HAVING → фильтрация после groupby.

-- Аналог оконных функций в pandas? -- groupby().transform() — для агрегации без сжатия. groupby().cumsum() — нарастающая сумма. groupby().rank() — ранги. groupby().shift()LAG.

-- Когда лучше SQL, когда pandas? -- SQL — когда данные в БД и задача решается запросом (JOIN, GROUP BY, WHERE). Pandas — когда нужен EDA, визуализация, сложные трансформации или данные из файлов.


Потренируйтесь решать задачи — откройте тренажёр с 1500+ вопросами для подготовки к собеседованиям аналитиков.

FAQ

Что учить первым — SQL или pandas?

SQL. 80% работы аналитика — SQL-запросы. Pandas — второй приоритет, для дальнейшего анализа и визуализации. Подробнее — в roadmap аналитика.

Можно ли заменить SQL на pandas?

Технически да (через pd.read_csv + анализ в памяти). Практически нет: pandas не масштабируется на большие данные, не поддерживает конкурентный доступ, не хранит данные на диске. SQL и pandas — комплементарные инструменты.

А что насчёт Polars?

Polars — современная альтернатива pandas на Rust: быстрее, экономнее по памяти, ленивые вычисления. Синтаксис отличается. Пока pandas доминирует в вакансиях и на собеседованиях, но Polars набирает популярность.

Как тренироваться

Решайте задачи на SQL и pandas параллельно — в тренажёре Карьерник есть вопросы по обоим инструментам. Больше вопросов — в разделе с примерами.