Короткий ответ
ORM даёт единообразие, безопасность от SQL-инъекций «из коробки», переносимость и скорость разработки типовых CRUD-операций, но на сложных аналитических запросах может генерировать неоптимальный SQL; raw SQL (или материализованное представление/хранимая процедура) даёт полный контроль над производительностью, но жертвует переносимостью и типобезопасностью ORM и требует отдельного процесса тестирования SQL-кода.
Как это работает подробнее
ORM строит SQL динамически на основе объектной модели и может генерировать как эффективный запрос для простых случаев, так и избыточные JOIN/подзапросы или N+1 без явного указания стратегий загрузки; raw SQL пишется и оптимизируется напрямую под конкретную СУБД с полным доступом к её специфичным возможностям (window functions, CTE).
Сравнение
- Компромисс. Решение нужно принимать по конкретному узкому месту, подтверждённому профилированием (EXPLAIN ANALYZE), а не предположениями «ORM всегда медленный»; критерии — частота изменения запроса, поддерживаемость для команды с разным уровнем знания SQL, необходимость поддержки нескольких СУБД и реальная разница в производительности на сопоставимых с production данных.
Пример кода
example.py
from sqlalchemy import text
report_sql = text("""
SELECT department_id,
date_trunc('month', hired_at) AS month,
COUNT(*) AS hires,
AVG(salary) AS avg_salary
FROM employees
WHERE hired_at >= :since
GROUP BY department_id, date_trunc('month', hired_at)
ORDER BY department_id, month
""")
rows = session.execute(report_sql, {"since": "2025-01-01"}).all()
# Проверка: сравнить EXPLAIN ANALYZE этого запроса с эквивалентным ORM-запросом на реальном объёме данныхЧто использовать на практике
- Практика. Распространённый компромисс — использовать ORM для основной части приложения, но выделять конкретные «горячие» или сложные аналитические запросы в raw SQL или материализованные представления, обновляемые по расписанию, если отчёт не обязан быть в реальном времени; важно сохранять такие запросы тестируемыми и версионируемыми, как остальной код.
Подводные камни
- Ошибка. Переписывают в raw SQL весь слой доступа к данным из-за одного медленного отчёта, теряя защиту от инъекций на остальном коде; или упорно остаются на чистом ORM для действительно тяжёлого запроса, тратя недели на попытки «уговорить» ORM сгенерировать эффективный SQL.