Выполнение SQL-запросов из Pandas
Выполняйте произвольные инструкции SELECT с помощью pd.read_sql_query и безопасно параметризуйте запросы, чтобы избежать SQL-инъекций.
«Выполнение SQL-запросов из Pandas» — бесплатный урок Pandas & NumPy Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Pandas & NumPy Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Pandas & NumPy Academy содержит 4 уроков всего.
pd.read_sql: единый интерфейс
Pandas предоставляет три функции для чтения из SQL: pd.read_sql() (универсальная оболочка), pd.read_sql_table() (считывает полную таблицу по имени) и pd.read_sql_query() (выполняет произвольный SQL). Для большинства аналитических процессов pd.read_sql_query() наиболее мощна, поскольку позволяет написать любую инструкцию SELECT с фильтрацией, объединением и агрегацией до того, как данные попадут в Pandas. Использование SQL для основных операций, а Pandas — для итогового анализа часто эффективнее, чем загрузка всего набора данных и фильтрация в Python.
import pandas as pd
import sqlalchemy as sa
engine = sa.create_engine('sqlite:///ecommerce.db')
# Three equivalent patterns
df1 = pd.read_sql('SELECT * FROM orders LIMIT 100', con=engine)
df2 = pd.read_sql_table('orders', con=engine) # full table
df3 = pd.read_sql_query('SELECT * FROM orders LIMIT 100', con=engine)
print(df3.head())
print(df3.columns.tolist())Фильтрация на уровне базы данных
Всегда фильтруйте данные в SQL, а не загружайте всё и фильтруйте в Pandas. База данных с подходящими индексами может выполнить предложение WHERE для миллионов строк и вернуть только тысячи строк за миллисекунды, тогда как Pandas сначала пришлось бы загрузить гигабайты данных. Главное правило: передавайте предикаты в базу данных. Используйте WHERE для фильтрации строк, SELECT col1, col2 для выбора столбцов и LIMIT во время разработки, чтобы быстро просматривать результаты.
import pandas as pd
import sqlalchemy as sa
engine = sa.create_engine('sqlite:///sales.db')
# Filter and project at SQL level — only fetch what you need
query = '''
SELECT order_id, customer_id, amount, status
FROM orders
WHERE status = 'completed'
AND order_date >= '2024-01-01'
AND amount > 50
LIMIT 1000
'''
df = pd.read_sql_query(query, con=engine)
print(f'Rows: {len(df)}, Columns: {list(df.columns)}')Агрегация в SQL и Pandas
Для простых сводок по группам в больших таблицах агрегации SQL производительнее Pandas, поскольку движок базы данных может использовать индексы, параллельное выполнение и хеш-агрегацию на диске. Используйте SQL для GROUP BY и SUM/COUNT/AVG, если таблица большая. Загрузите агрегированный результат (небольшой DataFrame) в Pandas для дальнейшего анализа, визуализации или объединения с другими данными. Для сложных пользовательских агрегаций, которые нельзя выразить в SQL, загрузите отфильтрованное подмножество в Pandas и применяйте там groupby.
import pandas as pd
import sqlalchemy as sa
engine = sa.create_engine('sqlite:///sales.db')
# Aggregate in SQL — returns a small result set
query = '''
SELECT region,
COUNT(*) AS order_count,
ROUND(SUM(amount), 2) AS total_revenue,
ROUND(AVG(amount), 2) AS avg_order_value
FROM orders
WHERE status = 'completed'
GROUP BY region
ORDER BY total_revenue DESC
'''
df = pd.read_sql_query(query, con=engine)
print(df)JOIN в SQL-запросах
Операции SQL JOIN эффективнее, чем merge() в Pandas, при объединении больших таблиц, поскольку база данных может использовать поиск по индексам. Напишите объединение в SQL и получите в Pandas предварительно объединённый, возможно уже отфильтрованный результат. При анализе нескольких таблиц один SQL-запрос с несколькими JOIN обычно быстрее, чем отдельное чтение каждой таблицы с последующим объединением в Pandas, особенно если одна из таблиц содержит миллионы строк, а объединение значительно сокращает результат.
import pandas as pd
import sqlalchemy as sa
engine = sa.create_engine('sqlite:///ecommerce.db')
query = '''
SELECT o.order_id,
c.customer_name,
c.country,
p.product_name,
o.amount
FROM orders o
JOIN customers c ON o.customer_id = c.id
JOIN products p ON o.product_id = p.id
WHERE o.status = 'completed'
LIMIT 500
'''
df = pd.read_sql_query(query, con=engine)
print(df.head())Использование переменных Python в запросах
Чтобы безопасно передавать переменные Python в SQL-запросы, используйте text() SQLAlchemy с именованными параметрами. Определите заполнители с помощью :param_name в строке запроса и передайте словарь в аргумент params функции read_sql_query. Это работает как с отдельными значениями, так и — в некоторых базах данных — со списками. Не используйте f-строки или форматирование % для создания строк запросов из переменных: это небезопасно даже во внутренних скриптах.
import pandas as pd
import sqlalchemy as sa
engine = sa.create_engine('sqlite:///sales.db')
# Python variables to inject
min_amount = 200.0
start_date = '2024-01-01'
end_date = '2024-12-31'
query = sa.text('''
SELECT * FROM orders
WHERE amount > :min_amount
AND order_date BETWEEN :start_date AND :end_date
''')
with engine.connect() as conn:
df = pd.read_sql_query(query, con=conn,
params={'min_amount': min_amount,
'start_date': start_date,
'end_date': end_date})
print(f'{len(df)} orders found')Использование CTE и подзапросов
Для сложного анализа часто требуются обобщённые табличные выражения (CTE) или подзапросы. Они полностью поддерживаются pd.read_sql_query — просто передайте всю многочастную инструкцию SQL как строку запроса. CTE (вводимые ключевым словом WITH) делают сложные запросы понятнее, присваивая имена промежуточным результатам. Это полезно для вычисления нарастающих итогов, ранжирования внутри групп и многоэтапной фильтрации, которые в Pandas потребовали бы громоздкого кода.
import pandas as pd
import sqlalchemy as sa
engine = sa.create_engine('sqlite:///sales.db')
query = '''
WITH monthly_revenue AS (
SELECT strftime('%Y-%m', order_date) AS month,
SUM(amount) AS revenue
FROM orders
WHERE status = 'completed'
GROUP BY month
)
SELECT month,
revenue,
revenue - LAG(revenue) OVER (ORDER BY month)
AS month_over_month_change
FROM monthly_revenue
ORDER BY month
'''
df = pd.read_sql_query(query, con=engine)
print(df.tail())Чтение с DatetimeIndex
При чтении временных рядов из базы данных задайте столбец с временными метками как индекс DataFrame, передав index_col='date_column' и parse_dates=['date_column'] в read_sql_query. Это сразу создаёт DatetimeIndex и позволяет выполнять в Pandas срезы по времени (df['2024-01']), ресемплирование и скользящие вычисления без дополнительных шагов обработки. Аргумент parse_dates указывает Pandas преобразовать столбец в datetime64.
import pandas as pd
import sqlalchemy as sa
engine = sa.create_engine('sqlite:///metrics.db')
df = pd.read_sql_query(
'SELECT recorded_at, metric_value FROM daily_metrics ORDER BY recorded_at',
con=engine,
index_col='recorded_at',
parse_dates=['recorded_at']
)
print(df.index.dtype) # datetime64[ns]
print(df['2024-06']) # Slice by month directlyПрофилирование медленных запросов
Если запрос выполняется медленно, добавьте ключевое слово SQL EXPLAIN (или EXPLAIN QUERY PLAN в SQLite) перед SELECT, чтобы увидеть план выполнения базы данных. Ищите полное сканирование таблиц ('SCAN TABLE') там, где ожидается поиск по индексам ('SEARCH TABLE'). Отсутствие индексов для столбцов WHERE и JOIN — наиболее распространённая причина медленных запросов. Создайте подходящий индекс в базе данных и повторно проверьте запрос с помощью EXPLAIN, прежде чем заново запускать конвейер Pandas.
import sqlalchemy as sa
engine = sa.create_engine('sqlite:///sales.db')
# Check if the query uses an index
with engine.connect() as conn:
plan = conn.execute(sa.text(
'EXPLAIN QUERY PLAN SELECT * FROM orders WHERE customer_id = 42'
)).fetchall()
for row in plan:
print(row)
# Look for 'SEARCH TABLE orders USING INDEX' — not 'SCAN TABLE'Постраничная обработка больших наборов результатов
При интерактивной итерации по большому набору результатов (например, при обработке одной страницы результатов за раз) используйте SQL LIMIT и OFFSET для реализации постраничной обработки. Извлекайте по N строк, обрабатывайте их, а затем извлекайте следующие N строк. Хотя этот подход менее эффективен, чем вариант с chunksize (который сохраняет курсор), постраничная обработка полезна, когда строки нужно постепенно отображать в отчёте или объединять результаты нескольких запросов.
import pandas as pd
import sqlalchemy as sa
engine = sa.create_engine('sqlite:///sales.db')
page_size = 10000
offset = 0
while True:
query = sa.text(
'SELECT * FROM orders ORDER BY order_id LIMIT :limit OFFSET :offset'
)
with engine.connect() as conn:
df = pd.read_sql_query(query, con=conn,
params={'limit': page_size, 'offset': offset})
if len(df) == 0:
break
print(f'Page at offset {offset}: {len(df)} rows')
offset += page_sizeОбъединение SQL-запросов с логикой Pandas
Наиболее мощный шаблон — это гибридный конвейер: используйте SQL для крупнозернистой фильтрации и агрегации, а затем Pandas для более тонких преобразований, которые неудобно выражать в SQL (сводные таблицы, разбор строк, функции apply, скользящие окна). Считайте из SQL управляемый по размеру набор результатов (тысячи строк), а затем объединяйте операции Pandas в цепочку для получившегося DataFrame. Это объединяет сильные стороны обоих инструментов и сохраняет поток данных в одном процессе Python.
import pandas as pd
import sqlalchemy as sa
engine = sa.create_engine('sqlite:///sales.db')
# SQL: coarse filter and join
df = pd.read_sql_query('''
SELECT o.customer_id, o.amount, o.order_date, c.country
FROM orders o JOIN customers c ON o.customer_id = c.id
WHERE o.status = 'completed'
''', con=engine, parse_dates=['order_date'])
# Pandas: rolling 30-day revenue per country
df = df.sort_values('order_date')
df['rolling_30d'] = (
df.groupby('country')['amount']
.transform(lambda x: x.rolling('30D').sum())
)
print(df.head())Обработка ошибок запросов к базе данных
Запросы к базе данных могут завершаться неудачно из-за сетевых тайм-аутов, синтаксических ошибок или потери подключений. Оборачивайте вызовы базы данных в блоки try-except, перехватывая sqlalchemy.exc.OperationalError для проблем с подключением и sqlalchemy.exc.ProgrammingError для синтаксических ошибок SQL. Записывайте ошибку в журнал вместе с контекстом (запросом и параметрами), а затем либо повторяйте попытку с экспоненциальным увеличением интервала, либо корректно завершайте операцию. В рабочих конвейерах важно отличать временные ошибки, допускающие повторную попытку, от постоянных ошибок, требующих исправления SQL.
import pandas as pd
import sqlalchemy as sa
engine = sa.create_engine('sqlite:///sales.db')
try:
df = pd.read_sql_query(
'SELECT * FROM nonexistent_table',
con=engine
)
except sa.exc.OperationalError as e:
print(f'Connection or table error: {e}')
except sa.exc.ProgrammingError as e:
print(f'SQL syntax error: {e}')
except Exception as e:
print(f'Unexpected error: {type(e).__name__}: {e}')Быстрая проверка
Проверьте, насколько хорошо вы поняли концепции анализа данных из этого урока.
Итоги урока
В этом уроке вы узнали, что pd.read_sql_query() выполняет любой SQL SELECT и возвращает DataFrame, передача фильтров и агрегаций в SQL эффективнее, чем загрузка полных таблиц в Pandas, а гибридные конвейеры объединяют SQL для крупномасштабного сокращения данных с Pandas для тонких пользовательских преобразований. Далее мы узнаем, как записывать DataFrames обратно в таблицы базы данных.
Изучай Python с ИИ-репетитором — бесплатно
Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.
- Курсы
- 30
- Уроки
- 120
Часто задаваемые вопросы
Урок «Выполнение SQL-запросов из Pandas» бесплатный?
Да — полный текст урока «Выполнение SQL-запросов из Pandas» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Pandas & NumPy Academy, подпишись на CoddyKit PRO. Курс Pandas & NumPy Academy содержит 4 уроков всего.
Чему я научусь в уроке «Выполнение SQL-запросов из Pandas»?
Выполняйте произвольные инструкции SELECT с помощью pd.read_sql_query и безопасно параметризуйте запросы, чтобы избежать SQL-инъекций. Ты практикуешь Pandas & NumPy Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Pandas & NumPy Academy?
Предыдущий опыт не требуется. Pandas & NumPy Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Выполнение SQL-запросов из Pandas»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Pandas & NumPy Academy?
Да. Каждый урок Pandas & NumPy Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Подключение к базе данных с помощью SQLAlchemy
- Выполнение SQL-запросов из Pandas
- Запись DataFrames в таблицы базы данных
- Pandas или SQL: выбор подходящего инструмента