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 параллельно — в тренажёре Карьерник есть вопросы по обоим инструментам. Больше вопросов — в разделе с примерами.