SQL на стероидах: как LLM превращает сложные запросы в понятный код
Каждый аналитик и разработчик знает: написать SQL-запрос — это полдела. Настоящая боль начинается, когда нужно разобраться в чужом EXPLAIN ANALYZE, переписать медленный JOIN или объяснить джуну, почему оконная функция с PARTITION BY работает не так, как он ожидал. LLM уже умеют генерировать SQL, но без правильного промта вы получите шаблонный ответ, который не учитывает особенности вашей схемы и версии PostgreSQL.
В этой подборке — 13 проверенных промтов для работы с реляционными базами. Они разбиты на три уровня: базовые (для повседневных задач), продвинутые (для оптимизации и сложной логики) и экспертные (для чтения планов и тонкой настройки). Каждый промт сопровождается примером запроса и пояснением, когда его применять.
Базовые промты: генерация и понимание запросов
1. Генерация сложного JOIN с пояснением
Задача: быстро собрать запрос с несколькими JOIN, не запутавшись в алиасах и условиях.
Промт:
Ты — опытный PostgreSQL-разработчик. Напиши SQL-запрос для схемы:
- users (id, name, email, created_at)
- orders (id, user_id, amount, status, created_at)
- order_items (id, order_id, product_id, quantity, price)
- products (id, name, category, price)
Нужно: вывести имя пользователя, email, общую сумму заказов за последние 30 дней и количество товаров в каждом заказе. Учти, что статус заказа может быть 'paid', 'pending', 'cancelled' — бери только 'paid'. Отсортируй по убыванию суммы. Объясни каждое соединение и условие.
Пример результата: LLM выдаст запрос с JOIN orders ON users.id = orders.user_id, LEFT JOIN order_items для подсчёта товаров и WHERE orders.created_at >= NOW() - INTERVAL '30 days' AND orders.status = 'paid'. В пояснении укажет, что LEFT JOIN нужен, чтобы не потерять заказы без товаров (хотя в реальности таких быть не должно — это повод проверить данные).
Когда применять: при написании запросов к незнакомой схеме или когда нужно быстро получить рабочий каркас.
2. Объяснение чужого запроса простым языком
Задача: разобраться, что делает запрос коллеги, который уволился полгода назад.
Промт:
Объясни, что делает этот SQL-запрос, простым языком для джуна. Разбери каждую часть: SELECT, FROM, JOIN, WHERE, GROUP BY, HAVING, ORDER BY. Укажи, какие данные он вернёт и какие могут быть подводные камни.
[вставьте запрос]
Пример результата: LLM разберёт запрос по строкам, укажет, что HAVING COUNT(*) > 5 фильтрует группы после агрегации, а ORDER BY без LIMIT может вернуть миллион строк. Часто такое объяснение выявляет скрытые баги — например, неявное приведение типов в WHERE.
Когда применять: при онбординге, ревью кода или рефакторинге унаследованной базы.
3. Конвертация запроса между диалектами
Задача: перенести запрос с MySQL на PostgreSQL (или наоборот) без потери логики.
Промт:
Перепиши этот MySQL-запрос для PostgreSQL. Сохрани всю логику, но используй синтаксис PostgreSQL. Обрати внимание на:
- функции работы с датами (DATE_FORMAT → TO_CHAR)
- LIMIT/OFFSET
- кавычки для идентификаторов
- обработку NULL в агрегатах
[вставьте запрос]
Пример результата: LLM заменит IFNULL на COALESCE, DATE_FORMAT(created_at, '%Y-%m') на TO_CHAR(created_at, 'YYYY-MM'), а также укажет, что в PostgreSQL GROUP BY требует явного перечисления неагрегированных колонок (если не используется функциональная зависимость от первичного ключа).
Когда применять: при миграции между СУБД или написании переносимого кода.
Продвинутые промты: оконные функции и оптимизация
4. Оконные функции для анализа данных
Задача: посчитать running total, rank и скользящее среднее без подзапросов.
Промт:
Напиши SQL-запрос с оконными функциями для таблицы sales (id, product_id, sale_date, amount). Нужно:
1. Для каждого product_id посчитать накопительную сумму продаж по дате (running total).
2. Присвоить ранг каждого product_id по общей сумме продаж (rank).
3. Вычислить скользящее среднее amount за последние 3 продажи для каждого product_id.
Используй OVER (PARTITION BY ... ORDER BY ...). Объясни разницу между ROWS и RANGE в рамке окна.
Пример результата: LLM сгенерирует запрос с SUM(amount) OVER (PARTITION BY product_id ORDER BY sale_date), RANK() OVER (ORDER BY SUM(amount) DESC) и AVG(amount) OVER (PARTITION BY product_id ORDER BY sale_date ROWS BETWEEN 2 PRECEDING AND CURRENT ROW). В пояснении укажет, что ROWS считает физические строки, а RANGE — логические группы (например, все продажи за один день).
Когда применять: для аналитики, отчётов и замены коррелированных подзапросов.
5. Поиск узких мест в индексах
Задача: понять, почему запрос медленный, и какие индексы добавить.
Промт:
Проанализируй этот запрос и предложи индексы для ускорения. Учти:
- какие колонки используются в WHERE, JOIN, ORDER BY
- селективность колонок
- тип индекса (B-tree, GIN, BRIN)
- порядок колонок в составном индексе
[вставьте запрос и схему таблиц]
Пример результата: LLM предложит составной индекс (status, created_at) для фильтрации по статусу и дате, объяснит, что для полнотекстового поиска нужен GIN, а для больших таблиц с диапазонными запросами — BRIN. Предупредит, что индекс по низкоселективной колонке (например, is_active) может не использоваться.
Когда применять: при тюнинге медленных запросов или проектировании схемы.
6. Переписывание запроса под большую нагрузку
Задача: убрать коррелированные подзапросы и заменить их на JOIN или оконные функции.
Промт:
Перепиши этот запрос так, чтобы он работал быстрее на большой таблице (10 млн строк). Избавься от коррелированных подзапросов, используй JOIN и оконные функции. Объясни, почему новая версия эффективнее.
[вставьте запрос]
Пример результата: LLM заменит SELECT (SELECT COUNT(*) FROM orders WHERE user_id = u.id) на LEFT JOIN (SELECT user_id, COUNT(*) FROM orders GROUP BY user_id) o ON u.id = o.user_id. В пояснении укажет, что коррелированный подзапрос выполняется для каждой строки, а GROUP BY + JOIN — один раз.
Когда применять: при оптимизации отчётов и дашбордов.
Экспертные промты: планы и тонкая настройка
7. Чтение EXPLAIN ANALYZE
Задача: разобрать план выполнения и найти самое дорогое место.
Промт:
Прочитай вывод EXPLAIN (ANALYZE, BUFFERS) для этого запроса. Найди узкие места: seq scan на больших таблицах, неэффективные join, высокий actual time. Объясни, что означает каждый узел плана и какие действия предпринять.
[вставьте вывод EXPLAIN]
Пример результата: LLM укажет, что Seq Scan on orders (cost=0.00..12345.67 rows=100000 width=50) (actual time=0.123..456.789 rows=100000 loops=1) означает полное сканирование, и предложит индекс. Обратит внимание на Buffers: shared hit=1000 read=5000 — высокое чтение с диска сигнализирует о нехватке кэша.
Когда применять: при диагностике медленных запросов в production.
8. Сравнение планов до и после оптимизации
Задача: убедиться, что изменения действительно ускорили запрос.
Промт:
Сравни два плана EXPLAIN ANALYZE: до и после добавления индекса. Обрати внимание на:
- изменение типа сканирования (Seq Scan → Index Scan)
- actual time и rows
- стоимость (cost)
- использование памяти (Work_Mem)
Сделай вывод, помог ли индекс.
Пример результата: LLM покажет, что время выполнения упало с 456 мс до 12 мс, но предупредит, что индекс может замедлить INSERT/UPDATE. Рекомендует проверить нагрузку на запись.
Когда применять: после внедрения оптимизаций для валидации эффекта.
9. Настройка autovacuum и analyze
Задача: понять, почему статистика устарела и планы стали плохими.
Промт:
Объясни, как настроить autovacuum для таблицы с интенсивными обновлениями. Какие параметры изменить (autovacuum_vacuum_scale_factor, autovacuum_analyze_scale_factor) и почему. Как проверить, что статистика актуальна?
Пример результата: LLM порекомендует уменьшить autovacuum_vacuum_scale_factor с 0.2 до 0.05 для часто обновляемых таблиц, чтобы vacuum запускался чаще. Покажет запрос к pg_stat_user_tables для проверки last_autovacuum и last_autoanalyze.
Когда применять: при деградации производительности без видимых причин.
10. Поиск блокировок и deadlock'ов
Задача: выявить, какие транзакции блокируют друг друга.
Промт:
Напиши SQL-запрос к системным представлениям PostgreSQL (pg_locks, pg_stat_activity), который покажет текущие блокировки и ожидающие транзакции. Объясни, как читать результат и находить источник deadlock'а.
Пример результата: LLM предложит запрос с JOIN между pg_locks и pg_stat_activity, который выведет pid, query, wait_event_type, wait_event. Объяснит, что wait_event_type = 'Lock' указывает на ожидание блокировки.
Когда применять: при зависаниях приложения или ошибках «deadlock detected».
Экспертные промты: продвинутая диагностика
11. Генерация тестовых данных для нагрузки
Задача: создать реалистичный датасет для бенчмарка.
Промт:
Сгенерируй SQL-скрипт для создания 1 млн строк в таблице orders (id, user_id, amount, status, created_at). Данные должны быть реалистичными: 80% заказов со статусом 'paid', даты за последний год, сумма от 100 до 10000. Используй generate_series.
Пример результата: LLM напишет INSERT INTO orders SELECT generate_series(1,1000000), (random()*1000)::int, (random()*9900+100)::numeric(10,2), CASE WHEN random() < 0.8 THEN 'paid' ELSE 'pending' END, NOW() - (random() * INTERVAL '365 days'). Это позволит протестировать запросы на объёме, близком к production.
Когда применять: перед оптимизацией, чтобы воспроизвести проблему на тестовой базе.
12. Аудит безопасности запросов
Задача: проверить, нет ли SQL-инъекций и опасных конструкций.
Промт:
Проверь этот SQL-запрос на безопасность. Есть ли риск SQL-инъекции? Используются ли параметризованные запросы? Есть ли опасные операции (DROP, TRUNCATE, DELETE без WHERE)? Предложи исправления.
[вставьте запрос]
Пример результата: LLM обнаружит конкатенацию строк в WHERE id = " + userInput + " и порекомендует использовать параметризованные запросы ($1). Укажет, что DELETE FROM logs без WHERE может удалить все данные.
Когда применять: при код-ревью или перед деплоем.
13. Документирование схемы и запросов
Задача: автоматически сгенерировать комментарии к таблицам и сложным запросам.
Промт:
Напиши комментарии к этой схеме БД: для каждой таблицы и колонки укажи назначение, тип данных и связи. Затем добавь комментарии к запросу, объяснив каждый шаг.
[вставьте DDL и запрос]
Пример результата: LLM сгенерирует COMMENT ON TABLE orders IS 'Заказы пользователей'; и пояснит, что order_items связана с orders по order_id. Для запроса добавит построчные комментарии, которые можно вставить в код.
Когда применять: при передаче проекта или для документации в репозитории.
Как использовать эти промты в работе
Эти 13 промтов покрывают основные сценарии: от генерации запросов до диагностики блокировок. Начните с базовых, чтобы ускорить повседневные задачи, затем переходите к продвинутым для оптимизации. Экспертные промты требуют больше контекста, но окупаются при работе с высоконагруженными системами.
Помните: LLM — это инструмент, а не замена пониманию SQL. Всегда проверяйте сгенерированные запросы на тестовых данных и сравнивайте планы выполнения. Для углублённого изучения баз данных и смежных тем загляните в Операционные системы и системное программирование в 2026 году, где есть материалы по системному программированию и инфраструктуре.