Вы когда-нибудь писали SQL-запрос, который выполнялся так долго, что успевали сходить за кофе, а он всё ещё крутился? Или проектировали схему базы данных, а через месяц понимали, что нормализация пошла не туда? Если да — вы не одиноки. Многие разработчики и аналитики тратят часы на написание и оптимизацию запросов, хотя могли бы делегировать рутину нейросети. Я собрал 12 проверенных промтов, которые превратят ChatGPT или другую LLM в вашего персонального DBA и аналитика. Это не магия, а инженерия: каждый промт я тестировал на реальных задачах, и теперь делюсь с вами.
Почему промты для SQL — это не игрушки
Прежде чем перейти к подборке, давайте разберёмся, почему нейросети вообще могут писать SQL. Современные LLM обучались на огромном количестве кода, включая дампы GitHub и Stack Overflow. Они знают синтаксис PostgreSQL, типичные ошибки и паттерны. Но главное — они понимают контекст. Если вы опишете структуру таблиц и задачу, модель сгенерирует запрос, который часто работает с первого раза. Конечно, бывают ошибки, но с правильными промтами их можно свести к минимуму.
Официальная документация PostgreSQL (https://www.postgresql.org/docs/) — ваш лучший друг. Я рекомендую всегда сверять сгенерированный код с ней, особенно если речь идёт о сложных оконных функциях или работе с JSONB. Нейросеть может предложить синтаксис, который устарел или не подходит для вашей версии, поэтому проверка обязательна.
Базовые промты: для тех, кто только начинает
1. Генерация запроса по описанию
Задача: вы описываете структуру таблиц и хотите получить готовый SQL.
Промт:
У меня есть таблицы в PostgreSQL:
- 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)
Напиши SQL-запрос, который выведет для каждого пользователя сумму его заказов за последние 30 дней. Включи только пользователей с суммой больше 1000. Отсортируй по сумме убыванию.
Результат:
SELECT
u.name,
SUM(o.amount) AS total_amount
FROM
users u
JOIN
orders o ON u.id = o.user_id
WHERE
o.created_at >= NOW() - INTERVAL '30 days'
AND o.status = 'completed'
GROUP BY
u.id, u.name
HAVING
SUM(o.amount) > 1000
ORDER BY
total_amount DESC;
Почему это работает: модель понимает, что нужно соединить таблицы, отфильтровать по дате и группировать. Обратите внимание на HAVING — это правильный способ фильтрации после агрегации. Если бы вы написали WHERE с суммой, получили бы ошибку.
2. Объяснение сложного запроса
Задача: вы нашли в коде запрос, который не понимаете, и хотите его расшифровать.
Промт:
Объясни, что делает этот SQL-запрос для PostgreSQL, шаг за шагом:
WITH ranked AS (
SELECT
product_id,
ROW_NUMBER() OVER (PARTITION BY category ORDER BY price DESC) AS rn
FROM products
)
SELECT * FROM ranked WHERE rn <= 3;
Результат:
Модель объяснит, что это оконная функция ROW_NUMBER(), которая нумерует продукты внутри каждой категории по убыванию цены, а затем выбирает топ-3 самых дорогих продукта в каждой категории. Она также может добавить примечание, что ROW_NUMBER() присваивает уникальные номера, и если есть одинаковые цены, порядок будет произвольным. Если это критично, лучше использовать RANK() или DENSE_RANK().
3. Исправление ошибки в запросе
Задача: у вас есть запрос, который выдаёт ошибку, и вы хотите понять, что не так.
Промт:
У меня есть запрос, который вызывает ошибку в PostgreSQL:
SELECT name, COUNT(*)
FROM users
JOIN orders ON users.id = orders.user_id
WHERE created_at > '2025-01-01'
GROUP BY name;
Ошибка: column "users.created_at" must appear in the GROUP BY clause or be used in an aggregate function. Как исправить?
Результат:
Модель объяснит, что в WHERE нельзя использовать агрегаты и что колонка created_at должна быть либо в GROUP BY, либо в агрегатной функции. Правильный вариант:
SELECT
u.name,
COUNT(*) AS order_count
FROM
users u
JOIN
orders o ON u.id = o.user_id
WHERE
o.created_at > '2025-01-01'
GROUP BY
u.id, u.name;
Также добавит совет: если нужно фильтровать по агрегату, используйте HAVING.
Продвинутые промты: для работы с производительностью и проектированием
4. Оптимизация медленного запроса
Задача: запрос выполняется слишком долго, нужно найти узкое место.
Промт:
У меня есть запрос, который выполняется 15 секунд на таблице с миллионом строк. Вот он:
SELECT *
FROM orders
WHERE user_id IN (SELECT id FROM users WHERE created_at > '2025-01-01');
Как его оптимизировать? Дайте конкретные советы и перепишите запрос, если нужно.
Результат:
Модель может предложить использовать JOIN вместо подзапроса, добавить индексы на user_id и created_at, а также избегать SELECT *. Пример:
SELECT
o.*
FROM
orders o
JOIN
users u ON o.user_id = u.id
WHERE
u.created_at > '2025-01-01';
И добавит: для максимальной производительности создайте индекс: CREATE INDEX idx_users_created_at ON users(created_at); и CREATE INDEX idx_orders_user_id ON orders(user_id);. Также стоит проверить план выполнения через EXPLAIN ANALYZE.
5. Проектирование схемы базы данных
Задача: вам нужно спроектировать схему для нового проекта.
Промт:
Спроектируй схему базы данных для интернет-магазина. Нужны таблицы для пользователей, товаров, корзины и заказов. Учитывай, что товары могут иметь несколько категорий, а пользователи — несколько адресов доставки. Используй PostgreSQL. Покажи SQL-код создания таблиц с ограничениями и индексами.
Результат:
Модель предложит связи многие-ко-многим для товаров и категорий, отдельную таблицу адресов, а также продумает внешние ключи и индексы. Например:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
price NUMERIC(10,2) NOT NULL CHECK (price >= 0),
stock INT NOT NULL DEFAULT 0
);
CREATE TABLE product_categories (
product_id INT REFERENCES products(id) ON DELETE CASCADE,
category_id INT REFERENCES categories(id) ON DELETE CASCADE,
PRIMARY KEY (product_id, category_id)
);
CREATE TABLE addresses (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id) ON DELETE CASCADE,
street TEXT NOT NULL,
city VARCHAR(100) NOT NULL,
zip_code VARCHAR(20)
);
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
address_id INT REFERENCES addresses(id),
status VARCHAR(20) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE order_items (
order_id INT REFERENCES orders(id) ON DELETE CASCADE,
product_id INT REFERENCES products(id),
quantity INT NOT NULL CHECK (quantity > 0),
price NUMERIC(10,2) NOT NULL,
PRIMARY KEY (order_id, product_id)
);
Это базовый, но рабочий вариант. Модель также может добавить триггер для обновления остатков.
6. Написание миграций
Задача: вам нужно изменить существующую таблицу, не потеряв данные.
Промт:
В моей таблице products есть колонка price типа NUMERIC. Мне нужно переименовать её в unit_price, а также добавить колонку currency VARCHAR(3) со значением по умолчанию 'USD'. Напиши SQL-миграцию для PostgreSQL, которая сделает это безопасно, с учётом возможного наличия зависимостей.
Результат:
Модель предложит использовать ALTER TABLE с проверкой существования, чтобы избежать ошибок. Например:
BEGIN;
ALTER TABLE products
RENAME COLUMN price TO unit_price;
ALTER TABLE products
ADD COLUMN IF NOT EXISTS currency VARCHAR(3) DEFAULT 'USD';
COMMIT;
Она также посоветует обновить все зависимые представления или код приложения.
Экспертные промты: для аналитиков и не только
7. Создание отчёта с группировками
Задача: сгенерировать отчёт по продажам с разбивкой по месяцам и категориям.
Промт:
У меня есть таблицы: orders, order_items, products, categories. Напиши запрос, который покажет для каждого месяца (за 2025 год) общую выручку по каждой категории товаров. Включи месяцы, в которых не было продаж, с нулевой выручкой. Используй generate_series для заполнения пропусков.
Результат:
WITH months AS (
SELECT generate_series('2025-01-01'::date, '2025-12-01'::date, '1 month') AS month_start
)
SELECT
to_char(m.month_start, 'YYYY-MM') AS month,
c.name AS category,
COALESCE(SUM(oi.quantity * oi.price), 0) AS revenue
FROM
months m
CROSS JOIN
categories c
LEFT JOIN
products p ON c.id = p.category_id
LEFT JOIN
order_items oi ON p.id = oi.product_id
LEFT JOIN
orders o ON oi.order_id = o.id AND date_trunc('month', o.created_at) = m.month_start AND o.status = 'completed'
GROUP BY
m.month_start, c.name
ORDER BY
month, category;
Здесь ключевая идея — generate_series и CROSS JOIN, чтобы получить все комбинации месяц-категория.
8. Анализ данных с помощью оконных функций
Задача: вычислить скользящее среднее или сравнить с предыдущим периодом.
Промт:
Используя таблицу orders (id, user_id, amount, created_at), напиши запрос, который для каждого дня в 2025 году выводит:
- сумму заказов за день
- скользящее среднее за 7 дней (включая текущий)
- разницу между текущим днём и предыдущим
Отсортируй по дате.
Результат:
WITH daily AS (
SELECT
created_at::date AS day,
SUM(amount) AS amount
FROM
orders
WHERE
created_at >= '2025-01-01' AND created_at < '2026-01-01'
GROUP BY
created_at::date
)
SELECT
day,
amount,
AVG(amount) OVER (ORDER BY day ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS avg_7d,
amount - LAG(amount) OVER (ORDER BY day) AS diff_prev
FROM
daily
ORDER BY
day;
Модель объяснит, что LAG обращается к предыдущей строке, а ROWS BETWEEN определяет рамки окна.
9. Поиск дубликатов и аномалий
Задача: найти дублирующиеся записи в таблице пользователей по email.
Промт:
В таблице users есть колонка email, но нет уникального ограничения. Найди все дубликаты email и выведи id всех пользователей с одинаковыми email.
Результат:
SELECT
email,
array_agg(id) AS user_ids,
COUNT(*) AS cnt
FROM
users
GROUP BY
email
HAVING
COUNT(*) > 1;
Модель также предложит вариант с оконной функцией, если нужно увидеть каждую строку отдельно, и посоветует добавить уникальный индекс после очистки.
10. Использование JSONB для гибких данных
Задача: работать с JSONB-полями, например, извлекать значения и фильтровать.
Промт:
У меня есть таблица events (id, user_id, payload JSONB). Напиши запрос, который выведет user_id и количество событий, где payload содержит ключ 'type' со значением 'click'. Также покажи, как извлечь значение 'page' из payload.
Результат:
SELECT
user_id,
COUNT(*) AS click_count,
payload->>'page' AS page
FROM
events
WHERE
payload->>'type' = 'click'
GROUP BY
user_id, payload->>'page';
Модель пояснит, что ->> возвращает текст, а -> возвращает JSONB. Для производительности стоит создать GIN-индекс: CREATE INDEX idx_events_payload ON events USING GIN(payload);.
11. Рекурсивные запросы для иерархий
Задача: обойти дерево категорий или структуру сотрудников.
Промт:
У меня есть таблица employees (id, name, manager_id). Напиши рекурсивный запрос, который выведет всех подчинённых конкретного менеджера (например, id=1), включая многоуровневых.
Результат:
WITH RECURSIVE subordinates AS (
SELECT id, name, manager_id
FROM employees
WHERE id = 1
UNION ALL
SELECT e.id, e.name, e.manager_id
FROM employees e
JOIN subordinates s ON e.manager_id = s.id
)
SELECT * FROM subordinates;
Это классический пример WITH RECURSIVE, который часто используется в организационных структурах.
12. Сравнение производительности двух вариантов запроса
Задача: понять, какой запрос эффективнее.
Промт:
Сравни два запроса для получения количества заказов по каждому пользователю. Первый использует подзапрос, второй — JOIN. Какой из них будет быстрее и почему? Покажи, как проверить с помощью EXPLAIN ANALYZE.
Результат:
Модель объяснит, что современный планировщик PostgreSQL может переписать подзапрос в JOIN, но в некоторых случаях JOIN будет эффективнее, особенно при наличии индексов. Она предложит использовать EXPLAIN ANALYZE:
EXPLAIN ANALYZE SELECT u.id, (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) FROM users u;
EXPLAIN ANALYZE SELECT u.id, COUNT(o.id) FROM users u LEFT JOIN orders o ON o.user_id = u.id GROUP BY u.id;
И посоветует сравнивать время выполнения и стоимость.
Как адаптировать промты под свою базу данных
Приведённые промты — это шаблоны. Чтобы получить максимальную пользу, адаптируйте их:
- Указывайте реальные имена таблиц и колонок. Чем точнее описание, тем лучше результат.
- Добавляйте контекст о версии PostgreSQL. Например,
PostgreSQL 15. - Просите объяснить, если что-то непонятно. Вы можете задать уточняющие вопросы.
- Всегда проверяйте сгенерированный код на тестовой базе. Никогда не запускайте непроверенный код на проде.
Вывод
Нейросети не заменят опытного DBA, но они могут снять с вас рутину и ускорить разработку. Используйте эти промты как отправную точку, адаптируйте под свои задачи и не забывайте проверять результат. Если вы хотите систематизировать знания и научиться применять ИИ в работе с базами данных, обратите внимание на наши курсы в блоге ASI Biont — там мы разбираем такие примеры на реальных кейсах. А теперь — идите и оптимизируйте свои запросы!
Комментарии