Проблема: медленные запросы и тонны рутины
В прошлый вторник я снова застрял на проде: аналитический отчёт по PostgreSQL выполнялся 47 секунд вместо ожидаемых двух. Клиент нервничал, дедлайн горел. Знакомо? По данным опроса Stack Overflow Developer Survey 2025, более 60% разработчиков тратят на написание и отладку SQL-запросов не менее 20% рабочего времени. А если добавить сюда MongoDB с её агрегациями, цифра становится пугающей.
Я перестал писать запросы вручную ещё полгода назад. Вместо этого я использую LLM как продвинутого ассистента: даю схему, контекст и цель — на выходе получаю готовый запрос с оптимизацией. За один вечер я переписал 12 медленных запросов, ускорив их в среднем в 8 раз. Вот мои проверенные промты, которые реально работают.
Промт 1: Генерация запроса по описанию
Зачем: Быстро получить рабочий SQL, когда лень вспоминать синтаксис оконных функций.
Промт:
Ты — эксперт по PostgreSQL 16. У меня есть таблицы:
- orders (id, user_id, total_amount, created_at, status)
- users (id, email, created_at, country)
Напиши запрос, который выведет для каждого пользователя сумму всех завершённых заказов за последние 30 дней, но только если сумма превышает 1000. Отсортируй по убыванию суммы. Используй CTE и оконную функцию для ранжирования.
Пример результата:
WITH recent_orders AS (
SELECT user_id, SUM(total_amount) AS total
FROM orders
WHERE status = 'completed'
AND created_at >= NOW() - INTERVAL '30 days'
GROUP BY user_id
)
SELECT u.email, ro.total,
RANK() OVER (ORDER BY ro.total DESC) AS rank
FROM recent_orders ro
JOIN users u ON u.id = ro.user_id
WHERE ro.total > 1000
ORDER BY ro.total DESC;
Совет: Всегда указывайте версию СУБД и полную схему — точность вырастает в разы.
Промт 2: Анализ EXPLAIN ANALYZE
Зачем: Понять, почему запрос тормозит, и получить конкретные шаги по оптимизации.
Промт:
Вот вывод EXPLAIN (ANALYZE, BUFFERS) для запроса. Объясни, что здесь является узким местом, и предложи, какие индексы создать или как переписать запрос. Приведи альтернативный вариант.
[вставляем вывод EXPLAIN]
Пример из практики: После такого промта я увидел, что PostgreSQL делает Seq Scan по таблице с 2 млн строк. Добавил составной индекс (user_id, status, created_at) — время выполнения упало с 47 секунд до 0.8 секунды.
Промт 3: Генерация агрегации MongoDB
Зачем: Сложные pipeline в MongoDB — это боль. LLM знает все стадии наизусть.
Промт:
Ты — эксперт по MongoDB 7.0. Коллекция events содержит документы вида:
{ _id, userId, eventType, timestamp, properties: { ... } }
Напиши aggregation pipeline, который:
1. Фильтрует события за последние 7 дней.
2. Группирует по userId и eventType.
3. Считает количество событий каждого типа.
4. Выводит только тех пользователей, у которых больше 10 событий.
5. Сортирует по убыванию общего количества.
Пример результата:
db.events.aggregate([
{ $match: { timestamp: { $gte: new Date(Date.now() - 72460601000) } } },
{ $group: { _id: { userId: "$userId", eventType: "$eventType" }, count: { $sum: 1 } } },
{ $group: { _id: "$_id.userId", total: { $sum: "$count" }, types: { $push: { type: "$_id.eventType", count: "$count" } } } },
{ $match: { total: { $gt: 10 } } },
{ $sort: { total: -1 } }
])
Промт 4: Оптимизация существующего запроса
Зачем: Когда запрос уже есть, но работает медленно.
Промт:
Вот SQL-запрос, который выполняется 12 секунд на таблице 5 млн строк. Предложи три варианта оптимизации: переписать запрос, добавить индексы, изменить схему. Для каждого варианта укажи ожидаемый прирост производительности.
SELECT * FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.country = 'RU'
AND o.created_at > '2025-01-01'
ORDER BY o.total_amount DESC
LIMIT 100;
Что я получил: Вариант с индексом (country, created_at, total_amount) и переписыванием на подзапрос дал ускорение в 15 раз.
Промт 5: Конвертация SQL в MongoDB aggregation
Зачем: Миграция с реляционной БД на документную — частая задача.
Промт:
Переведи следующий SQL-запрос в эквивалентный aggregation pipeline MongoDB. Учти, что в MongoDB нет JOIN, поэтому используй $lookup.
SELECT u.name, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.name
HAVING COUNT(o.id) > 5;
Результат:
db.users.aggregate([
{ $lookup: { from: "orders", localField: "_id", foreignField: "user_id", as: "orders" } },
{ $project: { name: 1, order_count: { $size: "$orders" } } },
{ $match: { order_count: { $gt: 5 } } }
])
Промт 6: Генерация миграции с изменением схемы
Зачем: Безопасно менять структуру таблиц на проде.
Промт:
Напиши миграцию для PostgreSQL, которая добавляет столбец email_verified BOOLEAN DEFAULT FALSE в таблицу users, создаёт индекс по этому столбцу и обновляет существующие записи на основе данных из таблицы verifications. Используй транзакцию.
Пример:
BEGIN;
ALTER TABLE users ADD COLUMN email_verified BOOLEAN DEFAULT FALSE;
UPDATE users u SET email_verified = TRUE
FROM verifications v WHERE v.user_id = u.id AND v.status = 'verified';
CREATE INDEX idx_users_email_verified ON users(email_verified);
COMMIT;
Промт 7: Написание тестовых данных
Зачем: Для нагрузочного тестирования нужны реалистичные данные.
Промт:
Сгенерируй SQL-скрипт для вставки 100 000 случайных записей в таблицу orders (user_id, total_amount, created_at, status). Используй generate_series и random(). Учти, что user_id должен ссылаться на существующих пользователей (id от 1 до 1000).
Результат:
INSERT INTO orders (user_id, total_amount, created_at, status)
SELECT
(random() * 999 + 1)::int,
(random() * 10000)::numeric(10,2),
NOW() - (random() * INTERVAL '365 days'),
(ARRAY['pending','completed','cancelled'])[floor(random()*3+1)]
FROM generate_series(1, 100000);
Промт 8: Поиск неоптимальных мест в схеме
Зачем: Иногда проблема не в запросе, а в структуре БД.
Промт:
Проанализируй схему моей базы данных (привожу DDL) и укажи:
- отсутствующие индексы для частых запросов,
- избыточные индексы,
- потенциальные проблемы с типами данных,
- возможности для денормализации.
[вставляем DDL]
Пример: LLM предложил заменить VARCHAR(255) на TEXT для полей с переменной длиной и добавить частичный индекс для активных пользователей.
Промт 9: Объяснение плана выполнения MongoDB
Зачем: explain() в MongoDB не так очевиден, как в SQL.
Промт:
Вот вывод db.collection.explain('executionStats') для aggregation pipeline. Объясни, какие стадии выполняются неэффективно, и предложи, как переписать pipeline или какие индексы создать.
[вставляем вывод explain]
Что я узнал: Оказалось, что $match стоял после $group, из-за чего обрабатывались все документы. Перенёс $match в начало — скорость выросла в 10 раз.
Итог: как я теперь работаю
Раньше я писал запросы методом тыка, потом часами смотрел в EXPLAIN. Теперь я трачу 5 минут на формулировку промта, получаю готовый запрос с оптимизацией и сразу проверяю его на тестовой среде. За вечер я переписал 12 запросов, среднее ускорение — 8x. Это не магия, а правильный инструмент в руках инженера.
Попробуйте эти промты на своих проектах. Начните с EXPLAIN ANALYZE — часто одного этого хватает, чтобы найти узкое место. А если хотите глубже — приходите на наш курс по базам данных, где мы разбираем реальные кейсы оптимизации. Делитесь своими находками в комментариях!
Комментарии