> Используете ли нативные SQL-запросы или ORM (PHP)
Уровень: senior · Роль: backend · Язык: PHP · Категория: Технические вопросы
Компании: vanta
Стек: PHP
> Пример ответа
Короткий ответ
В production-проектах я использую гибридный подход: ORM (Doctrine/Eloquent) для типовых CRUD-операций и нативные SQL-запросы для сложных выборок, отчётов и операций с высокой нагрузкой. Выбор зависит от конкретной задачи, а не от предпочтений. ORM даёт безопасность типов и маппинг, нативный SQL - контроль над планом выполнения и производительностью. Критично понимать, когда каждый из инструментов уместен.
Подробное объяснение
ORM и нативные SQL-запросы решают разные задачи. ORM абстрагирует работу с БД, предоставляя объектный доступ к данным, автоматические миграции, гидрацию сущностей и защиту от SQL-инъекций через query builder. Однако ORM генерирует неоптимальный SQL для сложных join'ов, агрегаций или оконных функций - часто получаются N+1 запросы или избыточные выборки.
Нативные SQL-запросы дают полный контроль над текстом запроса, позволяют использовать специфичные для конкретной СУБД возможности (например, LATERAL JOIN в PostgreSQL, FOR UPDATE SKIP LOCKED, полнотекстовый поиск). Минусы - ручное маппинг результатов, риск SQL-инъекций при неаккуратном использовании, потеря переносимости между СУБД.
Практическое правило: если запрос умещается в 2-3 join'а и не требует агрегаций - ORM. Если нужны подзапросы, оконные функции, динамические условия или работа с большими объёмами - нативный SQL. Также стоит учитывать, что в крупных системах часто используется CQRS: запись через ORM, чтение через оптимизированные SQL-запросы или даже отдельные read-модели.
На практике
В типичном PHP-проекте на Symfony/Doctrine я делаю так:
- Стандартные CRUD-операции, работа с агрегатами - через Doctrine repositories и DQL.
- Сложные отчёты, аналитика, экспорт данных - через
DBALс нативным SQL и параметризованными запросами. - Для высоконагруженных read-путей - raw SQL с явными индексами и
EXPLAIN. - В Laravel - Eloquent для простых моделей, Query Builder для средних по сложности запросов,
DB::select()для тяжёлых.
Важно: даже при использовании ORM я всегда проверяю сгенерированный SQL (в Doctrine - $query->getSQL(), в Laravel - toSql()), чтобы убедиться в отсутствии N+1 и лишних выборок.
Пример кода
Пример на Symfony/Doctrine, когда ORM не справляется - нужен нативный SQL для отчёта с оконной функцией:
PHP// Нативный SQL через DBAL$sql = "SELECTu.id,u.name,COUNT(o.id) AS order_count,SUM(o.total) AS total_sum,RANK() OVER (PARTITION BY u.region_id ORDER BY SUM(o.total) DESC) AS region_rankFROM users uLEFT JOIN orders o ON o.user_id = u.id AND o.created_at >= :start_dateWHERE u.active = 1GROUP BY u.id, u.name, u.region_idHAVING COUNT(o.id) > 0ORDER BY total_sum DESCLIMIT 100";$stmt = $this->connection->executeQuery($sql, ['start_date' => $startDate,]);$rows = $stmt->fetchAllAssociative();
А вот пример, где ORM уместен - простая выборка с фильтрацией:
PHP// Doctrine ORM$users = $this->entityManager->getRepository(User::class)->createQueryBuilder('u')->where('u.active = :active')->andWhere('u.createdAt > :date')->setParameter('active', true)->setParameter('date', $date)->orderBy('u.createdAt', 'DESC')->setMaxResults(50)->getQuery()->getResult();
Как отвечать на собеседовании
Начните с чёткого ответа: вы используете оба подхода, но осознанно. Затем приведите конкретные критерии выбора - сложность запроса, производительность, читаемость кода. Обязательно упомяните, что проверяете сгенерированный ORM SQL и используете EXPLAIN для нативных запросов. Хорошо добавить пример из практики: когда ORM создавал N+1 проблему и вы переписали на raw SQL, или наоборот - когда нативный SQL усложнял поддержку и вы перешли на ORM.
Покажите понимание trade-off: ORM - скорость разработки и безопасность, SQL - контроль и производительность. Если спросят про конкретный фреймворк - приведите детали реализации (Doctrine DBAL, Eloquent Query Builder). Не бойтесь признать, что иногда используете гибридные решения, например, raw SQL внутри repository-метода.
Что проверяет интервьюер
Интервьюер оценивает:
- Понимание сильных и слабых сторон обоих подходов, а не слепое следование одному из них.
- Умение принимать архитектурные решения на основе контекста задачи.
- Практический опыт: знает ли кандидат про N+1, lazy loading, пагинацию, индексы.
- Способность объяснить trade-off простыми словами и защитить свою позицию.
- Глубину: знает ли про query builder, DBAL, гидрацию, identity map.
Типичные ошибки
- Категоричное "я всегда использую только ORM" или "только нативные SQL" - показывает отсутствие гибкости.
- Утверждение, что ORM полностью защищает от SQL-инъекций - неверно, если использовать raw expressions или неправильно конкатенировать строки.
- Игнорирование производительности: кандидат не упоминает N+1, lazy loading, отсутствие проверки плана запроса.
- Использование ORM для сложных отчётов с join'ами на 5+ таблиц - признак отсутствия практического опыта.
- Неумение объяснить, когда именно стоит перейти на нативный SQL - интервьюер ждёт конкретных критериев, а не общих слов.
> Похожие задачи по PHP
Что такое составной индекс и как он работает
Зачем нужны блокировки в базе данных
Как хранятся индексы в массивах PHP
Что такое late static binding в PHP
> Похожие задачи по backend
Что такое составной индекс и как он работает
Зачем нужны блокировки в базе данных
Как хранятся индексы в массивах PHP
Что такое late static binding в PHP
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью