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.