Введение
SQL (Structured Query Language) остаётся одним из самых востребованных языков в IT, даже в 2026 году, когда AI-ассистенты стали частью повседневной работы разработчиков. По данным опроса Stack Overflow за 2025 год, SQL входит в топ-5 языков по использованию, и 65% респондентов отмечают, что пишут SQL-запросы хотя бы раз в неделю. Однако, как показывает практика, большинство проблем с производительностью баз данных связано не с плохим кодом, а с неоптимальными запросами, которые генерируют даже опытные программисты.
В этой статье я собрал 15 промтов для AI-моделей (от ChatGPT до Claude и Gemini), которые помогут вам не просто написать работающий запрос, но и оптимизировать его, понять планы выполнения, избежать типовых ошибок. Каждый промт — это конкретная задача с примером результата. Подборка разбита на три уровня: базовый (для тех, кто только знакомится с SQL), продвинутый (для ежедневных задач) и экспертный (для глубокой оптимизации и администрирования).
Все примеры основаны на стандартной схеме employees (таблицы orders, customers, products), чтобы вы могли легко протестировать их у себя. Поехали!
Базовые промты: для начинающих
1. Генерация запроса по описанию задачи
Задача: Преобразовать бизнес-требование в корректный SQL-запрос без знания синтаксиса.
Промт: «Напиши SQL-запрос к таблице orders, который покажет общую сумму заказов для каждого клиента за последний месяц. Таблица содержит поля: customer_id, order_date, amount. Используй GROUP BY и HAVING.»
Пример результата:
SELECT customer_id, SUM(amount) AS total_amount
FROM orders
WHERE order_date >= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1 month')
AND order_date < DATE_TRUNC('month', CURRENT_DATE)
GROUP BY customer_id
HAVING SUM(amount) > 0
ORDER BY total_amount DESC;
Этот запрос использует оконную функцию для группировки и фильтрации по дате. Для новичка важно, что AI объясняет каждую строку — вы можете попросить добавить комментарии.
2. Исправление синтаксической ошибки
Задача: Найти и исправить ошибку в неработающем запросе.
Промт: «Вот мой SQL-запрос: „SELECT * FROM orders WHERE order_date = ’2026-07-13’“. Он выдаёт ошибку. Исправь его и объясни причину.»
Пример результата: AI скажет, что кавычки должны быть одинарными, а не фигурными, и предложит: SELECT * FROM orders WHERE order_date = '2026-07-13'; — если поле типа date, то лучше использовать = '2026-07-13'::date.
3. JOIN для объединения таблиц
Задача: Объединить данные из двух таблиц без потери строк.
Промт: «Напиши LEFT JOIN для таблиц customers (id, name) и orders (customer_id, amount). Выведи всех клиентов, даже если у них нет заказов. Для клиентов без заказов покажи 0.»
Пример результата:
SELECT c.id, c.name, COALESCE(SUM(o.amount), 0) AS total_orders
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.name;
COALESCE — стандартная функция для замены NULL на 0.
Продвинутые промты: для ежедневной работы
4. Оптимизация медленного запроса
Задача: Ускорить запрос, который выполняется более 10 секунд на таблице с 5 миллионами строк.
Промт: «У меня есть запрос: SELECT * FROM orders WHERE customer_id IN (SELECT id FROM customers WHERE city = ’New York’). Он выполняется 12 секунд. Предложи оптимизацию с EXPLAIN ANALYZE и индексами.»
Пример результата: AI предложит заменить IN на JOIN и добавить индекс:
CREATE INDEX idx_customers_city ON customers(city);
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
SELECT o.*
FROM orders o
JOIN customers c ON o.customer_id = c.id
WHERE c.city = 'New York';
Согласно документации PostgreSQL, JOIN часто эффективнее подзапроса с IN, потому что планировщик может использовать hash join или merge join вместо последовательного сканирования.
5. Объяснение плана выполнения
Задача: Понять, почему запрос работает медленно, и исправить это.
Промт: «Вот результат EXPLAIN ANALYZE для моего запроса: [вставьте план]. Объясни, что означает каждая строка, и укажи, где bottleneck. Предложи исправления.»
Пример результата: AI покажет, что Seq Scan on orders (cost=0.00..45000.00 rows=1000000 width=100) — это полное сканирование таблицы из-за отсутствия индекса на order_date. Рекомендация: создать индекс CREATE INDEX idx_orders_date ON orders(order_date);.
6. Использование оконных функций для аналитики
Задача: Получить скользящее среднее или ранг без подзапросов.
Промт: «Напиши запрос, который для каждого заказа покажет его ранг по сумме внутри месяца, используя ROW_NUMBER. Таблица: orders (id, customer_id, amount, order_date).»
Пример результата:
SELECT id, customer_id, amount, order_date,
ROW_NUMBER() OVER (PARTITION BY DATE_TRUNC('month', order_date) ORDER BY amount DESC) AS rank_in_month
FROM orders;
Оконные функции — мощный инструмент, но их неправильное использование может привести к переполнению памяти. AI подскажет, что если таблица очень большая, стоит добавить ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW для оконных агрегатов.
7. Генерация тестовых данных
Задача: Наполнить таблицу случайными данными для тестирования.
Промт: «Сгенерируй SQL-скрипт для вставки 1000 случайных заказов в таблицу orders. Используй GENERATE_SERIES и random().»
Пример результата:
INSERT INTO orders (customer_id, amount, order_date)
SELECT
(random() * 100)::int + 1,
(random() * 1000)::numeric(10,2),
CURRENT_DATE - (random() * 365)::int
FROM generate_series(1, 1000);
Этот скрипт создаёт 1000 записей с клиентами от 1 до 100, случайными суммами и датами за последний год.
Экспертные промты: для глубокой оптимизации и администрирования
8. Анализ фрагментации индексов
Задача: Проверить, какие индексы нуждаются в реиндексации.
Промт: «Напиши запрос для PostgreSQL, который покажет степень фрагментации всех индексов в схеме public. Отфильтруй только те, где фрагментация > 30%.»
Пример результата:
SELECT
schemaname,
tablename,
indexname,
round(100 * pg_stat_user_indexes.idx_scan / NULLIF(pg_stat_user_indexes.idx_tup_read, 0), 2) AS index_usage_pct,
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
WHERE schemaname = 'public'
AND pg_stat_user_indexes.idx_scan IS NOT NULL
AND pg_stat_user_indexes.idx_tup_read > 0
HAVING index_usage_pct < 10;
Этот запрос показывает индексы, которые редко используются. Согласно документации PostgreSQL, такие индексы стоит удалить или перестроить.
9. Оптимизация запроса с помощью материализованных представлений
Задача: Ускорить сложный агрегирующий запрос, который выполняется каждые 5 минут.
Промт: «У меня есть запрос: SELECT customer_id, COUNT(*), SUM(amount) FROM orders GROUP BY customer_id. Он выполняется 15 секунд. Предложи использовать материализованное представление с обновлением по расписанию.»
Пример результата:
CREATE MATERIALIZED VIEW mv_customer_summary AS
SELECT customer_id, COUNT(*) AS order_count, SUM(amount) AS total_amount
FROM orders
GROUP BY customer_id;
-- Обновление раз в 10 минут
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_customer_summary;
Материализованные представления хранят результат запроса физически. Время выполнения сокращается до миллисекунд, но требует места на диске.
10. Использование CTE для рекурсивных запросов
Задача: Построить иерархию подчинённых в таблице employees.
Промт: «Таблица employees содержит id, name, manager_id. Напиши рекурсивный CTE для вывода всех подчинённых конкретного менеджера (id=1).»
Пример результата:
WITH RECURSIVE subordinate_tree AS (
SELECT id, name, manager_id, 1 AS level
FROM employees
WHERE id = 1
UNION ALL
SELECT e.id, e.name, e.manager_id, st.level + 1
FROM employees e
JOIN subordinate_tree st ON e.manager_id = st.id
)
SELECT * FROM subordinate_tree ORDER BY level, name;
Рекурсивные CTE — мощный инструмент, но важно настроить лимит глубины, чтобы избежать бесконечного цикла.
11. Партиционирование таблиц
Задача: Оптимизировать запросы к таблице orders с миллиардами записей.
Промт: «Таблица orders содержит 2 миллиарда записей. Запросы по дате выполняются минуты. Предложи схему партиционирования по месяцам и напиши DDL.»
Пример результата:
CREATE TABLE orders (
id BIGSERIAL,
customer_id INT,
amount DECIMAL,
order_date DATE
) PARTITION BY RANGE (order_date);
CREATE TABLE orders_2026_07 PARTITION OF orders
FOR VALUES FROM ('2026-07-01') TO ('2026-08-01');
-- и так для каждого месяца
Партиционирование позволяет планировщику PostgreSQL сканировать только нужные разделы, сокращая время запроса с минут до секунд.
12. Диагностика блокировок
Задача: Найти и устранить взаимные блокировки (deadlocks).
Промт: «Напиши запрос для PostgreSQL, который покажет все активные блокировки с информацией о заблокированном и блокирующем процессе.»
Пример результата:
SELECT
blocked_locks.pid AS blocked_pid,
blocked_activity.usename AS blocked_user,
blocking_locks.pid AS blocking_pid,
blocking_activity.usename AS blocking_user,
blocked_activity.query AS blocked_statement,
blocking_activity.query AS blocking_statement
FROM pg_locks blocked_locks
JOIN pg_stat_activity blocked_activity ON blocked_locks.pid = blocked_activity.pid
JOIN pg_locks blocking_locks ON blocked_locks.locktype = blocking_locks.locktype
AND blocked_locks.database IS NOT DISTINCT FROM blocking_locks.database
AND blocked_locks.relation IS NOT DISTINCT FROM blocking_locks.relation
AND blocked_locks.page IS NOT DISTINCT FROM blocking_locks.page
AND blocked_locks.tuple IS NOT DISTINCT FROM blocking_locks.tuple
AND blocked_locks.virtualxid IS NOT DISTINCT FROM blocking_locks.virtualxid
AND blocked_locks.transactionid IS NOT DISTINCT FROM blocking_locks.transactionid
AND blocked_locks.classid IS NOT DISTINCT FROM blocking_locks.classid
AND blocked_locks.objid IS NOT DISTINCT FROM blocking_locks.objid
AND blocked_locks.objsubid IS NOT DISTINCT FROM blocking_locks.objsubid
AND blocked_locks.pid != blocking_locks.pid
JOIN pg_stat_activity blocking_activity ON blocking_locks.pid = blocking_activity.pid
WHERE NOT blocked_locks.granted;
Этот запрос из официальной документации PostgreSQL помогает быстро локализовать проблему.
13. Использование JSON-функций
Задача: Извлечь данные из JSON-поля без нормализации схемы.
Промт: «Таблица events содержит поле metadata типа JSONB. Напиши запрос, который извлечёт все события, где metadata->>'source' = 'web', и выведет вложенное поле user_agent.»
Пример результата:
SELECT id, metadata->>'user_agent' AS user_agent
FROM events
WHERE metadata @> '{"source": "web"}'::jsonb;
Использование @> (contains) быстрее, чем ->> для фильтрации.
14. Оптимизация с помощью индексов GIN для полнотекстового поиска
Задача: Реализовать быстрый поиск по текстовому полю.
Промт: «Таблица products содержит поле description (TEXT). Напиши запрос для поиска по нескольким словам с ранжированием по релевантности, используя GIN-индекс.»
Пример результата:
CREATE INDEX idx_products_fts ON products USING GIN(to_tsvector('english', description));
SELECT id, name, ts_rank(to_tsvector('english', description), plainto_tsquery('laptop gaming')) AS rank
FROM products
WHERE to_tsvector('english', description) @@ plainto_tsquery('laptop gaming')
ORDER BY rank DESC LIMIT 10;
GIN-индексы ускоряют полнотекстовый поиск в десятки раз.
15. Генерация отчёта по использованию ресурсов БД
Задача: Получить сводку по самым затратным запросам.
Промт: «Напиши запрос для pg_stat_statements, который покажет топ-10 запросов по общему времени выполнения с планом.»
Пример результата:
SELECT query, total_time, calls, mean_time, rows
FROM pg_stat_statements
ORDER BY total_time DESC
LIMIT 10;
Расширение pg_stat_statements должно быть включено (shared_preload_libraries). Это стандартный инструмент для мониторинга производительности.
Заключение
Промты — это не замена знаниям, а мощный инструмент для ускорения работы. Я показал 15 сценариев, от базовой генерации до экспертной диагностики блокировок и партиционирования. Главный совет: всегда проверяйте сгенерированный код на тестовой среде, особенно если запрос модифицирует данные. По данным исследования Redgate (2025), 40% разработчиков хотя бы раз сталкивались с потерей данных из-за неверно сгенерированного SQL.
Для углублённого изучения рекомендую официальную документацию PostgreSQL (postgresql.org/docs) и книгу «SQL Performance Explained» Маркуса Виннанда. А если хотите попрактиковаться в написании сложных запросов с реальными данными — создайте тестовую БД и прогоните примеры из статьи. Удачи в оптимизации!
Комментарии