Ответ
Использование нативного SQL оправдано, когда ORM (Hibernate, JPA) генерирует неэффективные запросы или не поддерживает специфичные функции СУБД.
Ключевые сценарии:
-
Сложные аналитические запросы (OLAP):
- Оконные функции (
ROW_NUMBER(),LAG(),LEAD(), агрегаты сOVER). - Рекурсивные запросы (
WITH RECURSIVE). - Группировки и агрегация с
CUBE,ROLLUP,GROUPING SETS.-- Пример: ранжирование с помощью оконной функции SELECT user_id, order_date, amount, SUM(amount) OVER (PARTITION BY user_id ORDER BY order_date) AS running_total FROM orders WHERE order_date >= '2024-01-01';
- Оконные функции (
-
Массовые операции (Bulk Updates/Deletes):
- ORM может загружать сущности в память, что неэффективно. Нативный SQL выполнит одну операцию на стороне БД.
-- Эффективное обновление UPDATE orders SET status = 'ARCHIVED' WHERE created_at < '2023-01-01';
- ORM может загружать сущности в память, что неэффективно. Нативный SQL выполнит одну операцию на стороне БД.
-
Использование специфичных функций СУБД:
- Геопространственные функции (PostGIS в PostgreSQL).
- Полнотекстовый поиск (
tsvector/tsqueryв PostgreSQL,MATCH ... AGAINSTв MySQL). - JSON/XML функции для нереляционных данных внутри БД.
-
Оптимизация производительности критичных запросов:
- Когда DBA или разработчик должен явно контролировать план выполнения, использовать хинты или определенные индексы.
-
Создание и модификация схемы БД (DDL):
- Создание сложных индексов (частичных, составных), триггеров, материализованных представлений.
Как использовать в Java (Spring JPA):
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
// Использование @Query с nativeQuery = true
@Query(value =
"SELECT * FROM orders o " +
"WHERE o.region_id = :regionId " +
"AND earth_distance(ll_to_earth(o.lat, o.lng), ll_to_earth(:lat, :lng)) < :radius",
nativeQuery = true)
List<Order> findOrdersInRadius(@Param("regionId") Long regionId,
@Param("lat") double latitude,
@Param("lng") double longitude,
@Param("radius") double radiusMeters);
// Массовое обновление через @Modifying
@Modifying
@Query(value = "UPDATE orders SET priority = :newPriority WHERE status = 'PENDING'",
nativeQuery = true)
@Transactional
int bulkUpdatePriority(@Param("newPriority") int priority);
}
Риски: Нативный SQL теряет переносимость между СУБД и может быть подвержен SQL-инъекциям, если не использовать параметризованные запросы.