Pandas vs SQL — шпаргалка: одна задача, два решения
Зачем сравнивать
SQL и pandas решают одни и те же задачи: выбрать данные, отфильтровать, сгруппировать, объединить таблицы. На собеседованиях аналитиков одну и ту же задачу могут попросить решить сначала на SQL, а потом на Python. Если вы думаете в терминах одного языка — эта шпаргалка поможет быстро «перевести» на другой.
Таблица соответствий
| Операция | SQL | Pandas |
|---|---|---|
| Выбор столбцов | SELECT col1, col2 |
df[['col1', 'col2']] |
| Фильтрация | WHERE col > 5 |
df[df['col'] > 5] |
| Сортировка | ORDER BY col DESC |
df.sort_values('col', ascending=False) |
| Ограничение строк | LIMIT 10 |
df.head(10) |
| Уникальные строки | SELECT DISTINCT col |
df['col'].drop_duplicates() |
| Количество уникальных | COUNT(DISTINCT col) |
df['col'].nunique() |
| Группировка | GROUP BY col |
df.groupby('col') |
| Условие по агрегату | HAVING COUNT(*) > 1 |
фильтр после groupby |
| Объединение таблиц | JOIN |
df.merge() |
| Объединение строк | UNION ALL |
pd.concat() |
| Условная логика | CASE WHEN |
np.where() / apply |
| Оконные функции | SUM() OVER (PARTITION BY) |
groupby().transform() |
Теперь — подробнее с примерами.
SELECT — выбор столбцов
SELECT user_id, revenue FROM orders;df[['user_id', 'revenue']]Для одного столбца в pandas — одинарные скобки: df['revenue']. Вернётся Series, не DataFrame.
WHERE — фильтрация
SELECT * FROM orders WHERE revenue > 1000 AND status = 'paid';df[(df['revenue'] > 1000) & (df['status'] == 'paid')]Скобки вокруг каждого условия обязательны. & — И, | — ИЛИ, ~ — НЕ. Аналог IN — метод .isin():
df[df['city'].isin(['Москва', 'Питер'])]ORDER BY — сортировка
SELECT * FROM orders ORDER BY revenue DESC, created_at ASC;df.sort_values(['revenue', 'created_at'], ascending=[False, True])LIMIT — ограничение выборки
SELECT * FROM orders LIMIT 5;df.head(5)Для пропуска первых строк (OFFSET) — df.iloc[10:20].
GROUP BY + агрегация
SELECT city, COUNT(*) AS cnt, AVG(revenue) AS avg_rev
FROM orders
GROUP BY city;df.groupby('city').agg(
cnt=('revenue', 'count'),
avg_rev=('revenue', 'mean')
).reset_index()Подробнее — в гайде по groupby в Pandas.
HAVING — фильтр после группировки
SELECT city, COUNT(*) AS cnt
FROM orders
GROUP BY city
HAVING COUNT(*) > 100;result = df.groupby('city').size().reset_index(name='cnt')
result[result['cnt'] > 100]В pandas нет прямого аналога HAVING — сначала группируем, потом фильтруем результат. Тот же принцип, но в два шага.
JOIN — объединение таблиц
SELECT o.*, u.name
FROM orders o
LEFT JOIN users u ON o.user_id = u.id;df.merge(users, left_on='user_id', right_on='id', how='left')| SQL | Pandas (how=) |
|---|---|
INNER JOIN |
'inner' |
LEFT JOIN |
'left' |
RIGHT JOIN |
'right' |
FULL OUTER JOIN |
'outer' |
Подробнее — в гайде по merge в Pandas.
UNION — объединение строк
SELECT * FROM orders_2024
UNION ALL
SELECT * FROM orders_2025;pd.concat([orders_2024, orders_2025], ignore_index=True)UNION (без ALL) удаляет дубли. Аналог: pd.concat(...).drop_duplicates().
CASE WHEN — условная логика
SELECT *,
CASE WHEN revenue > 1000 THEN 'high'
WHEN revenue > 300 THEN 'mid'
ELSE 'low'
END AS segment
FROM orders;import numpy as np
df['segment'] = np.where(
df['revenue'] > 1000, 'high',
np.where(df['revenue'] > 300, 'mid', 'low')
)Для сложных условий — np.select:
conditions = [
df['revenue'] > 1000,
df['revenue'] > 300
]
choices = ['high', 'mid']
df['segment'] = np.select(conditions, choices, default='low')DISTINCT и COUNT DISTINCT
SELECT DISTINCT city FROM orders;
SELECT COUNT(DISTINCT city) FROM orders;df['city'].drop_duplicates() # уникальные значения
df['city'].nunique() # количество уникальныхДля уникальных строк по нескольким столбцам: df.drop_duplicates(subset=['city', 'status']).
Оконные функции
ROW_NUMBER / RANK
SELECT *, ROW_NUMBER() OVER (PARTITION BY city ORDER BY revenue DESC) AS rn
FROM orders;df['rn'] = df.groupby('city')['revenue'].rank(method='first', ascending=False).astype(int)Нарастающая сумма
SELECT *, SUM(revenue) OVER (PARTITION BY city ORDER BY created_at) AS cum_rev
FROM orders;df['cum_rev'] = df.sort_values('created_at').groupby('city')['revenue'].cumsum()LAG / LEAD
SELECT *, LAG(revenue, 1) OVER (PARTITION BY user_id ORDER BY created_at) AS prev_rev
FROM orders;df['prev_rev'] = df.sort_values('created_at').groupby('user_id')['revenue'].shift(1)shift(1) — LAG, shift(-1) — LEAD. Подробнее про оконные функции — в гайде по SQL.
Когда лучше SQL
- Большие данные — SQL работает на стороне сервера, pandas тянет всё в память. Миллиарды строк — только SQL.
- Готовая инфраструктура — BI-инструменты, отчёты, ETL-пайплайны — всё построено на SQL.
- Собеседования — SQL спрашивают в 95% случаев. Это приоритет.
- Скорость — запрос к оптимизированной базе быстрее, чем загрузка CSV и обработка в pandas.
Когда лучше Pandas
- Прототипирование — Jupyter-ноутбук быстрее, чем писать SQL + подключаться к базе.
- Сложные трансформации — apply, пользовательские функции, regex по столбцам — в pandas проще.
- Визуализация — из DataFrame сразу в matplotlib/seaborn. Из SQL — только через промежуточный шаг.
- Данные не в базе — CSV, Excel, API — pandas загружает всё.
На собеседовании
Частый формат: дают задачу и просят решить на обоих языках. Например: «Найдите топ-3 города по выручке за последний месяц». Если вы решили на SQL за минуту, а на pandas зависли — это сигнал о слабом Python. И наоборот.
Потренируйтесь переводить запросы из одного языка в другой. Примеры вопросов из нашей базы — хорошая отправная точка. Или откройте тренажёр и порешайте задачи по SQL и Python прямо в Telegram.