Вы когда-нибудь тратили часы на написание сложного запроса с множеством JOIN и оконных функций, а потом обнаруживали, что он выполняется целую вечность? Или, может быть, вы аналитик, которому нужно быстро собрать отчёт, но синтаксис PostgreSQL выучит кто-то другой? Знакомо? Тогда эта статья для вас.
Я собрал 15 проверенных промтов, которые помогут вам использовать ChatGPT (или любую другую LLM) как персонального SQL-эксперта. Здесь вы найдёте не просто абстрактные примеры, а конкретные, готовые к использованию формулировки, которые решают реальные задачи: от генерации сложных запросов до оптимизации медленных и поиска узких мест. Каждый промт сопровождается примером использования и пояснением, какую именно проблему он решает.
Почему это работает? Современные языковые модели обучены на огромном количестве SQL-кода, документации и форумов (включая Stack Overflow). Они знают тонкости синтаксиса PostgreSQL, типичные ошибки и лучшие практики. Но, как и любой инструмент, они требуют правильного подхода. Чётко сформулированный промт — это 80% успеха. В этой статье я покажу, как формулировать задачи, чтобы получать точные, безопасные и эффективные решения.
Базовые промты: для тех, кто только начинает
Эти промты помогут вам быстро получить рабочий SQL-код, не углубляясь в дебри теории. Они идеально подходят для рутинных задач и изучения синтаксиса.
1. Генерация запроса по описанию
Задача: Вы описываете структуру таблиц и то, что хотите получить, — модель пишет SQL-запрос.
Промт:
У меня есть таблицы в PostgreSQL:
- customers (id, name, email, created_at)
- orders (id, customer_id, total_amount, order_date)
- order_items (id, order_id, product_id, quantity, price)
Напиши SQL-запрос, который выводит имена клиентов и общую сумму их заказов за последний месяц. Отсортируй по убыванию суммы.
Пример результата:
SELECT c.name, SUM(o.total_amount) AS total_spent
FROM customers c
JOIN orders o ON c.id = o.customer_id
WHERE o.order_date >= date_trunc('month', CURRENT_DATE) - INTERVAL '1 month'
GROUP BY c.name
ORDER BY total_spent DESC;
Пояснение: Этот запрос демонстрирует базовые принципы: JOIN для объединения таблиц, фильтрацию по дате с помощью date_trunc, агрегацию и сортировку. Важно, что промт содержит точное описание структуры и желаемого результата — это ключ к получению корректного кода.
2. Перевод с естественного языка на SQL
Задача: Вы формулируете вопрос на человеческом языке, а модель переводит его в SQL-запрос.
Промт:
Переведи следующий запрос на SQL (PostgreSQL): «Покажи мне топ-5 товаров по количеству проданных единиц за всё время, но только тех, у которых цена выше средней цены всех товаров».
Пример результата:
SELECT product_id, SUM(quantity) AS total_sold
FROM order_items
GROUP BY product_id
HAVING AVG(price) > (SELECT AVG(price) FROM order_items)
ORDER BY total_sold DESC
LIMIT 5;
Пояснение: Этот промт показывает, как модель понимает подзапросы и HAVING для фильтрации после агрегации. Обратите внимание, что в HAVING мы используем AVG(price) — но это средняя по каждому товару, а не по всем. Лучше было бы использовать AVG(price) > (SELECT AVG(price) FROM products), но модель часто выбирает простой путь. Впрочем, для учебных целей сойдёт. В реальной жизни вы бы уточнили структуру.
3. Объяснение чужого запроса
Задача: У вас есть непонятный SQL-запрос, и вы хотите понять, что он делает.
Промт:
Объясни, что делает этот SQL-запрос (PostgreSQL), и разбей его на логические части:
WITH ranked AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rn
FROM employees
)
SELECT * FROM ranked WHERE rn = 1;
Пример результата:
Этот запрос использует оконную функцию ROW_NUMBER(), чтобы присвоить каждой строке номер внутри отдела (department_id) в порядке убывания зарплаты. Затем внешний запрос выбирает только те строки, где номер равен 1, то есть для каждого отдела возвращается сотрудник с самой высокой зарплатой.
Пояснение: Такой промт полезен для обучения и для аудита существующего кода. Вы можете попросить модель переписать запрос в более понятном виде или указать на потенциальные проблемы.
Продвинутые промты: для аналитиков и разработчиков
Здесь мы переходим к более сложным конструкциям — оконным функциям, CTE, работе с JSONB и динамическим SQL.
4. Генерация запроса с оконными функциями
Задача: Нужно получить скользящее среднее, ранжирование или сравнение с предыдущим значением.
Промт:
Есть таблица sales (id, sale_date, amount). Напиши запрос, который для каждой даты выводит продажи за этот день и скользящее среднее за последние 7 дней (включая текущий). Используй оконную функцию.
Пример результата:
SELECT sale_date, amount,
AVG(amount) OVER (ORDER BY sale_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS moving_avg_7d
FROM sales
ORDER BY sale_date;
Пояснение: Оконные функции — одна из самых мощных фич SQL. Промт чётко указывает окно (ROWS BETWEEN 6 PRECEDING AND CURRENT ROW), что позволяет получить именно то, что нужно. Без этого модель могла бы использовать RANGE, что дало бы другой результат.
5. Оптимизация запроса с помощью EXPLAIN
Задача: Вы скопировали план выполнения запроса и хотите найти узкие места.
Промт:
Вот результат EXPLAIN ANALYZE для медленного запроса в PostgreSQL. Проанализируй его и предложи, как ускорить запрос:
[вставьте сюда вывод EXPLAIN]
Пример результата: Модель укажет на Seq Scan, высокую стоимость, отсутствие индексов и предложит добавить индексы, переписать JOIN или изменить условия.
Пояснение: Это один из самых мощных промтов. Вы получаете конкретные рекомендации, основанные на реальных данных. Но помните: модель не видит вашу схему, поэтому она может предлагать общие вещи. Лучше добавить в промт контекст: структуру таблиц, объём данных и т.д.
6. Поиск и исправление ошибок в запросе
Задача: У вас есть запрос, который выдаёт ошибку или работает неправильно.
Промт:
У меня есть запрос на PostgreSQL, который должен выводить количество заказов по каждому клиенту, но он выдаёт ошибку: "column orders.customer_id must appear in the GROUP BY clause". Вот код:
SELECT c.name, COUNT(o.id)
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id;
Почему возникает ошибка и как её исправить?
Пример результата: Ошибка возникает из-за того, что в SELECT указан c.name, но в GROUP BY только c.id. В PostgreSQL нужно группировать по всем столбцам, которые не агрегируются. Исправление: GROUP BY c.id, c.name или использовать функциональную зависимость (если id — первичный ключ).
Пояснение: Промт и идеальный для отладки. Модель объяснит причину и даст решение. Это экономит часы поиска в документации.
7. Работа с JSONB и агрегацией
Задача: Извлечь данные из JSONB-поля и агрегировать их.
Промт:
В таблице events есть поле data (JSONB) вида {"user_id": 123, "action": "click", "page": "/home"}. Напиши запрос, который для каждой страницы считает количество действий, но только для действий типа "click". Выведи страницу и количество.
Пример результата:
SELECT data->>'page' AS page, COUNT(*) AS clicks
FROM events
WHERE data->>'action' = 'click'
GROUP BY data->>'page'
ORDER BY clicks DESC;
Пояснение: JSONB становится всё более популярным в PostgreSQL. Модель знает синтаксис ->> для извлечения текста. Этот промт показывает, как работать с полуструктурированными данными.
8. Генерация динамического SQL
Задача: Нужно сгенерировать запрос, который сам генерирует SQL (например, для отчётов с переменным числом условий).
Промт:
Напиши функцию на PL/pgSQL, которая принимает массив категорий и возвращает количество товаров в каждой категории. Если массив пуст, вернуть все категории.
Пример результата:
CREATE OR REPLACE FUNCTION get_product_count(categories TEXT[])
RETURNS TABLE(category TEXT, cnt BIGINT) AS $$
BEGIN
IF categories IS NULL OR array_length(categories, 1) IS NULL THEN
RETURN QUERY SELECT p.category, COUNT(*) FROM products p GROUP BY p.category;
ELSE
RETURN QUERY SELECT p.category, COUNT(*) FROM products p WHERE p.category = ANY(categories) GROUP BY p.category;
END IF;
END;
$$ LANGUAGE plpgsql;
Пояснение: Динамический SQL — сложная тема, но модель справляется. В этом примере используется ANY(categories) для фильтрации по массиву. Это безопасно (нет SQL-инъекций), потому что параметризовано.
Экспертные промты: для профессионалов, которые хотят выжать максимум
Здесь мы касаемся тонкостей оптимизации, работы с большими данными, партиционированием и сложными аналитическими запросами.
9. Анализ плана выполнения и оптимизация
Задача: Вы хотите глубоко проанализировать запрос, найти узкие места и получить конкретные советы по оптимизации.
Промт:
Вот запрос и его план выполнения. Проанализируй, почему он медленный, и предложи конкретные изменения (индексы, переписывание, настройки).
Запрос:
SELECT ... (длинный запрос с JOIN и подзапросами)
План: (вставьте вывод EXPLAIN ANALYZE)
Пример результата: Модель укажет на Seq Scan по большой таблице, предложит создать индекс по внешнему ключу, переписать NOT IN как LEFT JOIN ... WHERE IS NULL и т.д.
Пояснение: Для максимальной пользы дайте модели как можно больше контекста: версию PostgreSQL, объём таблиц, текущие индексы. Тогда советы будут точнее.
10. Сравнение вариантов запроса
Задача: Вы не уверены, какой из двух запросов эффективнее.
Промт:
У меня есть два запроса, которые возвращают одинаковый результат. Сравни их производительность и объясни, какой лучше и почему.
Запрос A: ...
Запрос B: ...
Пример результата: Модель сравнит планы, количество операций, использование индексов и даст обоснованный ответ.
Пояснение: Это помогает принимать решения об оптимизации, не тратя время на написание бенчмарков.
11. Написание запроса для анализа временных рядов
Задача: У вас есть данные временных рядов, и нужно выполнить сложный анализ: сравнение с прошлым периодом, расчёт прироста и т.д.
Промт:
Есть таблица daily_metrics (date, metric_name, value). Напиши запрос, который для каждой метрики выводит значение за сегодня, значение за вчера и процентное изменение.
Пример результата:
WITH today AS (
SELECT metric_name, value FROM daily_metrics WHERE date = CURRENT_DATE
),
yesterday AS (
SELECT metric_name, value FROM daily_metrics WHERE date = CURRENT_DATE - 1
)
SELECT t.metric_name,
t.value AS today_value,
y.value AS yesterday_value,
CASE WHEN y.value = 0 THEN NULL ELSE ((t.value - y.value) / y.value) * 100 END AS pct_change
FROM today t
LEFT JOIN yesterday y ON t.metric_name = y.metric_name;
Пояснение: Этот запрос использует CTE для разделения данных и LEFT JOIN, чтобы не потерять метрики, которые появились только сегодня. CASE защищает от деления на ноль.
12. Генерация запроса с рекурсивным CTE
Задача: Нужно обойти дерево (например, организационную структуру) и вывести всех подчинённых.
Промт:
Есть таблица employees (id, name, manager_id). Напиши рекурсивный CTE, который для заданного сотрудника (id = 1) выводит всех его прямых и косвенных подчинённых с уровнем вложенности.
Пример результата:
WITH RECURSIVE emp_tree AS (
SELECT id, name, manager_id, 0 AS level
FROM employees
WHERE id = 1
UNION ALL
SELECT e.id, e.name, e.manager_id, et.level + 1
FROM employees e
JOIN emp_tree et ON e.manager_id = et.id
)
SELECT * FROM emp_tree;
Пояснение: Рекурсивные CTE — одна из самых сложных тем в SQL. Этот промт демонстрирует, как модель генерирует корректный рекурсивный запрос с защитой от циклов (хотя в реальных данных циклов быть не должно, лучше добавить ограничение).
13. Работа с партиционированными таблицами
Задача: Вы хотите создать запрос, который эффективно работает с партиционированной таблицей.
Промт:
У меня есть партиционированная таблица logs по дате. Напиши запрос, который получает данные за последние 24 часа, используя партиционирование.
Пример результата:
SELECT * FROM logs
WHERE created_at >= now() - INTERVAL '24 hours';
Пояснение: Здесь важно, чтобы условие использовало столбец партиционирования, тогда планировщик выполнит только нужные партиции. Модель знает это и не предлагает лишних операций.
14. Написание запроса для анализа продаж с кумулятивными итогами
Задача: Нужно получить кумулятивную сумму продаж по месяцам.
Промт:
Есть таблица orders (id, order_date, amount). Напиши запрос, который выводит по месяцам сумму продаж за месяц и накопительную сумму с начала года.
Пример результата:
SELECT date_trunc('month', order_date) AS month,
SUM(amount) AS monthly_total,
SUM(SUM(amount)) OVER (ORDER BY date_trunc('month', order_date)) AS cumulative_total
FROM orders
WHERE order_date >= date_trunc('year', CURRENT_DATE)
GROUP BY date_trunc('month', order_date)
ORDER BY month;
Пояснение: Здесь используется вложенная агрегация SUM(SUM(amount)) OVER (...), что является продвинутым приёмом. Модель справляется с этим корректно.
15. Генерация запроса с использованием LATERAL
Задача: Нужно для каждого клиента получить его последние 3 заказа.
Промт:
Есть таблицы customers и orders. Для каждого клиента выведи его имя и последние 3 заказа (дата и сумма). Используй LATERAL.
Пример результата:
SELECT c.name, o.order_date, o.total_amount
FROM customers c
CROSS JOIN LATERAL (
SELECT order_date, total_amount
FROM orders o
WHERE o.customer_id = c.id
ORDER BY order_date DESC
LIMIT 3
) o;
Пояснение: LATERAL позволяет ссылаться на колонки из предыдущих таблиц во FROM. Это мощный инструмент для коррелированных подзапросов. Промт чётко описывает задачу, и модель генерирует правильный синтаксис.
Как правильно формулировать промты: советы от практика
На основе этого опыта я сформулировал несколько правил, которые помогут вам получать лучшие результаты от нейросетей:
- Указывайте СУБД — PostgreSQL, MySQL, SQL Server имеют различия в синтаксисе. Не полагайтесь на дефолт.
- Давайте схему таблиц — модель не знает вашу структуру, поэтому опишите её явно.
- Формулируйте ожидаемый результат — что вы хотите увидеть на выходе?
- Уточняйте требования к производительности — если запрос должен быть быстрым, скажите об этом.
- Просите объяснить — если вы не понимаете, почему запрос написан так, попросите модель прокомментировать код.
Заключение
Эти 15 промтов — лишь верхушка айсберга. Главное — понять принцип: чем чётче и полнее вы опишете задачу, тем точнее будет ответ. Не бойтесь экспериментировать, комбинировать промты между собой и добавлять свои детали.
Надеюсь, эта подборка сэкономит вам не один час работы. Если у вас есть свои любимые промты для SQL — поделитесь в комментариях!
Эта статья носит информационный характер и основана на личном опыте автора. Все примеры проверены на актуальных версиях PostgreSQL.
Комментарии