Подписывайтесь:

Блог AST-SoftPro

PostgreSQL для Python: ORM vs raw SQL — что эффективнее?

24.05.2026 9 мин чтения
PostgreSQL для Python: ORM vs raw SQL — что эффективнее?

Введение в выбор подхода к работе с PostgreSQL из Python

Работа с базой данных — одна из ключевых задач при разработке приложений на Python. При использовании PostgreSQL часто возникают вопросы: использовать ли ORM (например, SQLAlchemy), писать прямые SQL-запросы или применять асинхронные драйверы? Каждый подход имеет свои преимущества и недостатки.

В этой статье мы рассмотрим три основных способа взаимодействия с PostgreSQL в Python:

  • Использование SQLAlchemy как ORM;

  • Прямой вызов raw SQL через psycopg2 или аналог;

  • Работа с помощью асинхронных драйверов, таких как asyncpg.

Также затронем тему управления схемами — миграциями, чтобы обеспечить согласованность базы данных при развитии приложения. Цель — помочь разработчику понять плюсы и минусы каждого метода на практике.

ORM: SQLAlchemy vs raw SQL — производительность и удобство

Что такое ORM?

Object-Relational Mapping (ORM) позволяет работать с базой данных через объекты Python, абстрагируясь от синтаксиса SQL. SQLAlchemy — один из самых популярных ORM для PostgreSQL в экосистеме Python.

Пример простого запроса:

db = create_engine('postgresql://user:pass@localhost/db')
session = sessionmaker(bind=db)()
result = session.query(User).filter_by(name='Alice').first()

ORM удобен для быстрого прототипирования, поддерживает сложные связи (many-to-many), lazy и eager загрузки данных. Однако при росте нагрузки на БД или необходимости оптимизации запросов его абстракция может стать «уткой», замедляющей выполнение.

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

  • При небольшом объёме данных;

  • В командах без SQL-специалистов — снижает порог входа;

  • Для простых CRUD-операций и API с ограниченными требованиями к производительности.

Однако ORM не всегда оптимален: он может генерировать избыточные запросы (N+1), не учитывает индексы или особенности структуры таблицы, особенно при наличии сложных JOIN'ов или агрегаций.

Пример проблемы N+1 в SQLAlchemy:

users = session.query(User).all()
for user in users:
    print(user.emails.count())  # Это вызовет отдельный запрос на каждый пользователя!

Решение — использовать subqueryload, contains_eager или явно писать SQL.

Raw SQL: контроль и производительность

Почему иногда нужен raw SQL?

Когда ORM не справляется, приходится возвращаться к прямым запросам. Это особенно актуально:

  • При необходимости максимальной оптимизации;

  • Для сложных аналитических запросов с большим числом JOIN'ов;

  • Когда нужно точно контролировать план выполнения (EXPLAIN ANALYZE);

  • В случаях, когда логика запроса слишком специфична для стандартной ORM.

Пример эффективного raw-запроса:

with db.connect() as conn:
    query = """
        SELECT u.id, COUNT(o.*) AS order_count 
        FROM users u 
        JOIN orders o ON u.id = o.user_id 
        GROUP BY u.id;
    """
    result = pd.read_sql(query, con=conn)

Такой запрос не будет генерироваться ORM — вы пишете его один раз и используете повторно.

Преимущества raw SQL:

  • Полный контроль над структурой запроса;

  • Возможность использовать CTE (WITH), оконные функции, materialized views;

  • Лучшее использование индексов при правильной структуре запроса;

  • Меньше накладных расходов на ORM — выше производительность в критичных сценариях.

Когда избегать raw SQL?

  • Если команда не имеет опыта написания эффективных запросов;

  • При быстрой разработке с частыми изменениями схемы;

  • В проектах, где важна скорость разработки больше, чем производительность (например, MVP).

Асинхронные драйверы: asyncpg и будущее PostgreSQL в Python

Проблема синхронности

Стандартный psycopg2 работает на блокирующих операциях. Это означает, что при выполнении запроса поток Python «зависает» до получения ответа — неэффективно для I/O-интенсивных задач (например, веб-сервисы с высокой нагрузкой).

Решение — использовать асинхронные драйверы, такие как asyncpg.

Пример:

import asyncpg
conn = await asyncpg.connect(
    user='user',
    password='pass',
    database='db',
    host='localhost'
)
rows = await conn.fetch('SELECT * FROM users WHERE age > $1', 18)
await conn.close()

Преимущества asyncpg:

  • Высокая пропускная способность — один поток обрабатывает множество запросов;

  • Поддержка async/await, совместимость с FastAPI, Starlette, aiohttp;

  • Реальное время выполнения: запросы не блокируют event loop;

  • Хорошая поддержка JSON, массивов PostgreSQL (например, jsonb).

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

  • В production-сервисах с высокой нагрузкой на БД;

  • При интеграции нескольких внешних систем через API;

  • Если проект уже использует асинхронную архитектуру.

Важно: использование asyncpg требует перехода всей архитектуры приложения в асинхронный режим — это не всегда оправдано для простых задач или прототипов.

Управление схемой: миграции и безопасность изменений

Проблема ручной модификации схемы БД

Часто разработчики создают таблицы вручную через psql или GUI-инструменты. При этом:

  • Нет контроля версий;

  • Риск ошибок при деплое (например, дублирование ключей);

  • Невозможно откатить изменение.

Решение — системы миграций.

SQLAlchemy Migrations vs Alembic vs Custom Scripts

SQLAlchemy предоставляет встроенный механизм sqlalchemy.migrate, но он не самый гибкий. Практика показывает предпочтение двум решениям:

  1. Alembic — стандартный инструмент для управления схемой в Python-проектах, совместимый с SQLAlchemy.

  2. Кастомные скрипты + Git + CI/CD — при больших изменениях или сложных бизнес-правилах (например, миграция данных).

Пример Alembic:

# env.py
def upgrade(migrate_fn, schema_namespace=''):
    # Миграция: добавление столбца age в таблицу users
current_op = op.add_column('users', sa.Column('age', sa.Integer))

Как выбрать подход к миграциям?

Сценарий Рекомендация
Малый проект, один разработчик Alembic + SQLAlchemy
Команда > 3 человек Alembic или Flyway (если не на Python)
Производственная среда с rollback’ами Кастомные скрипты + тестирование миграции в staging

Критерии выбора:

  • Возможность отката изменений;

  • Логирование всех шагов;

  • Тестирование миграций перед продакшеном;

  • Интеграция с CI/CD pipeline.

Сравнение подходов: таблица для быстрого анализа

Критерий SQLAlchemy ORM Raw SQL asyncpg
Производительность (высокая нагрузка) Средняя Высокая Очень высокая
Удобство разработки Высокое Низкое Среднее–низкое
Контроль над запросами Низкий Полный Средний
Поддержка асинхронности Нет Да Да
Безопасность (SQL-инъекции) Зависит от использования параметризованных запросов При ручной обработке — высокий риск Высокая при использовании параметров
Легкость поддержки Простота на старте, сложность в продакшене Требует SQL-экспертов Требует асинхронного кода

Рекомендации по выбору подхода

  1. Начинайте с SQLAlchemy — она даёт баланс между скоростью разработки и безопасностью.

  2. Переходите к raw SQL, когда ORM начинает тормозить (например, N+1 или сложные JOIN'ы).

  3. Используйте asyncpg, если проект уже асинхронный и нагрузка на БД критична.

  4. Всегда применяйте миграции — даже в небольших проектах это предотвращает ошибки при развёртывании.

Заключение

Выбор между ORM, raw SQL и асинхронными драйверами зависит от контекста:

  • На этапе прототипирования или малого проекта — SQLAlchemy идеален;

  • При росте нагрузки или сложности бизнес-задач — стоит переходить к более контролируемым методам (raw SQL + asyncpg);

  • В production-средах обязателен контроль версий схемы через миграции.

Нет универсального решения. Главное — понимать стоимость каждого подхода и выбирать его осознанно, исходя из требований проекта.

AI-Помощник