Ответ
Имею опыт написания и оптимизации сложных SQL-запросов (PostgreSQL, MySQL) и их аналогов в JPA (JPQL, Criteria API).
Пример сложного SQL-запроса с JOIN, агрегацией и подзапросом:
SELECT
u.id,
u.name,
COUNT(o.id) AS total_orders,
(SELECT MAX(amount) FROM payments p WHERE p.user_id = u.id) AS max_payment
FROM users u
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'COMPLETED'
WHERE u.created_at > '2023-01-01'
GROUP BY u.id, u.name
HAVING COUNT(o.id) > 5
ORDER BY total_orders DESC;
Ключевые навыки и best practices:
- Оптимизация: Анализ плана выполнения (
EXPLAIN ANALYZE), создание эффективных индексов, избегание N+1 проблемы. - Сложные конструкции: Оконные функции (
ROW_NUMBER(),RANK(),LAG/LEAD), рекурсивные CTE (WITH RECURSIVE), UNION. - Работа в JPA/Hibernate:
- Использование
@EntityGraphилиJOIN FETCHдля жадной загрузки и решения N+1. - Criteria API для построения динамических, типобезопасных запросов.
- Проекции DTO через
@Queryи конструктор выражений для выборки только нужных данных, минуя лишнюю работу контекста persistence.
- Использование
- Безопасность и работа с данными: Всегда использую параметризованные запросы (защита от SQL-инъекций), пагинацию (
LIMIT/OFFSET,keyset pagination) для больших наборов данных.
Ответ 18+ 🔞
А, опыт работы с SQL и JPA? Ну, это ж святое дело, блядь! Сидишь, бывает, перед монитором, а там запрос на три экрана, и думаешь — ёпта, кто эту хуйню писал? А потом вспоминаешь, что это ты полгода назад, и хочется ебать свои старые костыли.
Вот смотри, примерчик из жизни, когда нужно выцепить юзеров, которые не просто зарегились, а реально заказывали. И не просто заказывали, а чтобы больше пяти раз, да ещё и статус 'COMPLETED', чтобы не просто так пиздюли били. И заодно глянуть, какую максимальную сумму они отгрузили в платежах. Всё в одну кучу, блядь!
SELECT
u.id,
u.name,
COUNT(o.id) AS total_orders,
(SELECT MAX(amount) FROM payments p WHERE p.user_id = u.id) AS max_payment
FROM users u
LEFT JOIN orders o ON u.id = o.user_id AND o.status = 'COMPLETED'
WHERE u.created_at > '2023-01-01'
GROUP BY u.id, u.name
HAVING COUNT(o.id) > 5
ORDER BY total_orders DESC;
Выглядит вроде ничего, но под капотом-то, сука, может такая дичь начаться... Особенно если индексов нет, или JOIN'ы криво написаны. Одна N+1 проблема чего стоит — приложение хуярит запросы как сумасшедшее, а БД просто накрывается медным тазом.
Чем, собственно, маюсь и что умею:
- Оптимизация — это религия. Без
EXPLAIN ANALYZEвообще не подхожу к запросу. Создать индекс — это не простоCREATE INDEX, это надо понять, куда его ебашить, чтобы он реально работал, а не просто место жрал. И эта долбаная N+1... Её либоJOIN FETCHв JPQL, либо@EntityGraph— только так, иначе пиши пропало. - Сложные штуки тоже в ходу. Оконные функции — это ж магия, блядь!
ROW_NUMBER(),RANK()— когда нужно ранжировать или выбрать топ чего-нибудь. Рекурсивные CTE — для древовидных структур, там вообще мозг можно сломать, но красиво получается. - В мире JPA/Hibernate тоже не лаптем щи хлебаю.
- Criteria API — это мой друг, когда запрос нужно собирать по кусочкам, в зависимости от фильтров. Динамически, типобезопасно, без этих ужасных конкатенаций строк, где в любой момент можно накосячить.
- Проекции — отдельная песня. Зачем тащить целую сущность со всеми её ленивыми связями, если нужны три поля? Правильно, нихуя не зачем. Поэтому либо DTO через конструктор в
@Query, либо какие-нибудь интерфейсы Spring Data. Контекст persistence не засоряется, память не сосёт — красота.
- Безопасность и голова. Параметризованные запросы — это аксиома. Даже думать не буду вставлять значения напрямую в строку, иди нахуй с такими инъекциями. Пагинация — тоже must have.
LIMIT/OFFSETдля простого, а если данные активно обновляются, то keyset pagination (или курсоры) — чтобы не прыгал общий счётчик, как угорелый.
Короче, задача — не просто написать запрос, который работает. Задача — написать запрос, который работает быстро, безопасно и не заставляет сервер хрипеть, как паровоз. А то бывает такое напишут, что хоть святых выноси, ёпта.