SQL, но без боли: 15 промтов, которые заменят DBA в вашей команде

SQL, но без боли: 15 промтов, которые заменят DBA в вашей команде

Вы когда-нибудь просыпались в 3 часа ночи от мысли: «Почему мой запрос работает 40 секунд?»? Я — да. После того как я однажды случайно положил прод-базу на 10 минут, я понял: пора делегировать рутину нейросетям. Теперь я общаюсь с базой через промты, и она (почти) не обижается.

Эта подборка — не сборник «магических заклинаний», а рабочие инструменты для аналитиков и бэкенд-разработчиков. Каждый промт решает конкретную задачу: от генерации сложных JOIN до проектирования схемы. Проверено на PostgreSQL, MySQL и ClickHouse в бою.

1. Генерация запроса по описанию на естественном языке

Задача: превратить словесное описание в рабочий SQL.

Напиши SQL-запрос для PostgreSQL, который выводит топ-10 клиентов по сумме заказов за последний месяц. Таблицы: customers(id, name), orders(id, customer_id, amount, created_at). Учитывай только оплаченные заказы (status = 'paid').

Пример использования:

SELECT c.name, SUM(o.amount) AS total
FROM customers c
JOIN orders o ON o.customer_id = c.id
WHERE o.created_at >= date_trunc('month', CURRENT_DATE) - INTERVAL '1 month'
  AND o.status = 'paid'
GROUP BY c.id, c.name
ORDER BY total DESC
LIMIT 10;

2. Оптимизация существующего запроса через EXPLAIN

Задача: найти узкие места в медленном запросе.

Вот EXPLAIN ANALYZE моего запроса. Найди проблему и предложи оптимизацию (индексы, переписывание). Запрос: SELECT * FROM orders WHERE customer_id = 42 ORDER BY created_at DESC LIMIT 20;

Пример использования:

Объяснение: планировщик использует Seq Scan по orders, потому что нет индекса на customer_id. Рекомендация: CREATE INDEX idx_orders_customer_created ON orders(customer_id, created_at DESC);

3. Проектирование схемы БД для нового сервиса

Задача: спроектировать таблицы под описанный функционал.

Спроектируй схему для интернет-магазина: товары, категории, заказы, пользователи. Учитывай связи многие-ко-многим, внешние ключи, индексы. Дай DDL для PostgreSQL.

Пример использования:

CREATE TABLE categories (id SERIAL PRIMARY KEY, name TEXT NOT NULL);
CREATE TABLE products (id SERIAL PRIMARY KEY, category_id INT REFERENCES categories(id), name TEXT NOT NULL, price NUMERIC(10,2));
CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT UNIQUE NOT NULL);
CREATE TABLE orders (id SERIAL PRIMARY KEY, user_id INT REFERENCES users(id), created_at TIMESTAMP DEFAULT NOW());
CREATE TABLE order_items (order_id INT REFERENCES orders(id), product_id INT REFERENCES products(id), quantity INT CHECK (quantity > 0), PRIMARY KEY (order_id, product_id));

4. Написание миграций с откатом

Задача: создать безопасную миграцию для изменения схемы.

Напиши миграцию для добавления колонки discount NUMERIC(5,2) DEFAULT 0 в таблицу products. Дай версию для PostgreSQL с условием IF NOT EXISTS и откатом.

Пример использования:

-- Up
ALTER TABLE products ADD COLUMN IF NOT EXISTS discount NUMERIC(5,2) DEFAULT 0;
-- Down
ALTER TABLE products DROP COLUMN IF EXISTS discount;

5. Перевод запроса между диалектами SQL

Задача: адаптировать запрос из PostgreSQL в MySQL или ClickHouse.

Переведи этот запрос из PostgreSQL в ClickHouse: SELECT customer_id, COUNT(*) FROM orders WHERE created_at >= NOW() - INTERVAL '7 days' GROUP BY customer_id;

Пример использования:

SELECT customer_id, count() FROM orders WHERE created_at >= now() - INTERVAL 7 DAY GROUP BY customer_id;

6. Генерация тестовых данных

Задача: создать набор синтетических данных для проверки.

Сгенерируй SQL для вставки 1000 тестовых записей в таблицу users(email, name, created_at). Используй generate_series для PostgreSQL.

Пример использования:

INSERT INTO users (email, name, created_at)
SELECT 'user'

|| i || '@test.com', 'User ' || i, NOW() - (i || ' days')::interval
FROM generate_series(1, 1000) AS i;

7. Анализ производительности через EXPLAIN ANALYZE

Задача: получить разбор выполнения запроса.

Дай команду EXPLAIN ANALYZE для этого запроса и объясни, что означает каждая часть вывода: SELECT * FROM orders WHERE amount > 100;

Пример использования:

EXPLAIN ANALYZE SELECT * FROM orders WHERE amount > 100;
Результат: Seq Scan on orders (cost=0.00..35.50 rows=5 width=36) (actual time=0.024..0.038 rows=5 loops=1)
Объяснение: cost — оценка затрат, rows — число строк, actual time — реальное время.

8. Оптимизация JOIN и подзапросов

Задача: улучшить сложный запрос с несколькими JOIN.

Этот запрос с тремя JOIN работает медленно: SELECT ... FROM a JOIN b ON ... JOIN c ON ... WHERE a.type = 'x'. Предложи альтернативу с EXISTS или CTE.

Пример использования:

-- Было
SELECT DISTINCT a.id FROM a JOIN b ON a.id = b.a_id JOIN c ON b.id = c.b_id WHERE c.status = 'active';
-- Стало
SELECT a.id FROM a WHERE EXISTS (SELECT 1 FROM b JOIN c ON c.b_id = b.id WHERE b.a_id = a.id AND c.status = 'active');

9. Работа с оконными функциями

Задача: написать запрос с ROW_NUMBER, LAG, SUM(OVER) для аналитики.

Напиши запрос для PostgreSQL: для каждого клиента выведи его заказы с порядковым номером по дате и разницей в днях между соседними заказами. Таблицы: orders(id, customer_id, created_at).

Пример использования:

SELECT customer_id, id, created_at,
       ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY created_at) AS rn,
       created_at - LAG(created_at) OVER (PARTITION BY customer_id ORDER BY created_at) AS days_diff
FROM orders;

10. Диагностика блокировок и взаимоблокировок

Задача: найти и решить проблему с блокировками в PostgreSQL.

Дай запрос для поиска активных блокировок в PostgreSQL и объясни, как их снять.

Пример использования:

SELECT pid, state, wait_event_type, wait_event, query
FROM pg_stat_activity
WHERE state = 'active' AND wait_event_type IS NOT NULL;
-- Для принудительного завершения: SELECT pg_terminate_backend(pid);

11. Генерация отчёта с группировкой по периодам

Задача: создать отчёт по продажам с группировкой по дням/неделям/месяцам.

Напиши запрос для ClickHouse: количество заказов и сумма выручки по дням за последние 30 дней. Таблица: orders(created_at DateTime, amount Decimal).

Пример использования:

SELECT toDate(created_at) AS day, count() AS orders, sum(amount) AS revenue
FROM orders
WHERE created_at >= now() - INTERVAL 30 DAY
GROUP BY day
ORDER BY day;

12. Объяснение плана выполнения запроса

Задача: понять, что делает планировщик.

Объясни этот план выполнения простыми словами: 'Index Scan using idx_orders_customer on orders (cost=0.28..8.30 rows=1 width=12)'.

Пример использования:

Это означает, что PostgreSQL использует индекс idx_orders_customer для поиска строк по customer_id, что эффективнее полного сканирования. cost — относительная оценка затрат, rows — ожидаемое число строк.

13. Написание функции или хранимой процедуры

Задача: создать функцию для повторяющегося расчёта.

Создай функцию в PostgreSQL: calculate_discount(price NUMERIC, user_rank TEXT), которая возвращает цену со скидкой: 'gold' — 20%, 'silver' — 10%, иначе 0.

Пример использования:

CREATE OR REPLACE FUNCTION calculate_discount(price NUMERIC, user_rank TEXT)
RETURNS NUMERIC AS $$
BEGIN
  RETURN price * CASE user_rank WHEN 'gold' THEN 0.8 WHEN 'silver' THEN 0.9 ELSE 1 END;
END;
$$ LANGUAGE plpgsql;

14. Выявление дубликатов и аномалий в данных

Задача: найти повторяющиеся записи или некорректные значения.

Напиши запрос для поиска дубликатов email в таблице users, оставив только один ID для каждого дубликата.

Пример использования:

SELECT email, COUNT(*) AS cnt, array_agg(id) AS ids
FROM users
GROUP BY email
HAVING COUNT(*) > 1;
-- Удаление дубликатов: DELETE FROM users WHERE id NOT IN (SELECT MIN(id) FROM users GROUP BY email);

15. Рекомендации по индексации

Задача: понять, какие индексы нужны для ускорения запросов.

Вот типичные запросы к таблице events: WHERE user_id = ? AND created_at > ? ORDER BY created_at DESC. Какие индексы посоветуешь?

Пример использования:

CREATE INDEX idx_events_user_created ON events(user_id, created_at DESC);
-- Покрывающий индекс, если нужно только эти колонки:
CREATE INDEX idx_events_user_created_covering ON events(user_id, created_at DESC) INCLUDE (event_type);

Как я использовал эти промты в реальной работе

Недавно мне нужно было выгрузить данные о поведении пользователей за год. Запрос с тремя JOIN выполнялся 15 секунд. Я скормил его промту №2, получил рекомендацию по индексу, создал его через миграцию (промт №4) — время упало до 0.2 секунды. Сэкономил себе час ожидания и нервов.

Для ClickHouse я использую промт №11 для ежедневных отчётов — он генерирует точные запросы с учётом синтаксиса, а не «примерно похожие». Это критично, потому что ClickHouse не поддерживает стандартный SQL на 100%.

Пять правил безопасного использования промтов

  1. Всегда проверяй на копии базы. Промты могут предлагать DROP TABLE или DELETE без WHERE.
  2. Уточняй версию СУБД. PostgreSQL 16 и MySQL 8 имеют разные синтаксисы для одних и тех же задач.
  3. Используй EXPLAIN до выполнения. Попроси нейросеть сразу выдать план.
  4. Не доверяй слепо. Сверяй с документацией: PostgreSQL, ClickHouse.
  5. Изолируй права. Для промтов, которые меняют данные, используй отдельного пользователя с ограниченными правами.

Что дальше

Соберите свою библиотеку промтов — я сохранил эти 15 в заметки и добавляю новые по мере появления задач. Начните с трёх самых частых: генерация (№1), оптимизация (№2), и индексы (№15). Они закрывают 80% ежедневных потребностей.

Если хотите научиться составлять такие промты системно, на платформе Asibiont есть курсы по работе с ИИ в разработке — но это уже другая история. Пока просто скопируйте промт, вставьте в ChatGPT или Claude, и ваша база скажет вам спасибо.

А какой запрос вы мечтаете оптимизировать? Напишите в комментариях — обсудим.

← All posts

Comments