SQL-промты, которые сэкономят часы: от сложных JOIN до тонкой оптимизации в PostgreSQL и MySQL

Вы когда-нибудь тратили полдня на написание запроса, который потом всё равно выполнялся 15 минут? Или получали от бизнеса задачу «дай выгрузку по клиентам с их последними заказами», а в голове всплывала каша из JOIN и подзапросов? Знакомо. SQL — язык мощный, но порой коварный: один неудачный JOIN превращает быстрый запрос в пожирателя ресурсов, а неоптимальный индекс — в причину ночных инцидентов. Хорошая новость: современные LLM (большие языковые модели) умеют писать и оптимизировать SQL ничуть не хуже опытного разработчика, если правильно сформулировать промт. В этой статье я собрал 15 проверенных промтов, которые помогут вам быстрее решать типовые задачи — от написания сложных выборок до профилирования и настройки производительности. Каждый промт снабжён примером использования и пояснением, как он работает. Погнали.

Базовые промты: уверенный старт

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

Задача: Быстро получить рабочий SQL-запрос, не вспоминая синтаксис.

Промт:

Напиши SQL-запрос для PostgreSQL, который выводит список клиентов (таблица customers) с количеством их заказов (таблица orders) за последние 30 дней. Включи только клиентов, у которых более 5 заказов. Отсортируй по убыванию количества заказов.

Пример результата:

SELECT 
    c.customer_id,
    c.name,
    COUNT(o.order_id) AS order_count
FROM customers c
JOIN orders o ON c.customer_id = o.customer_id
WHERE o.order_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY c.customer_id, c.name
HAVING COUNT(o.order_id) > 5
ORDER BY order_count DESC;

Почему это работает: LLM понимает контекст и генерирует корректный синтаксис с учётом диалекта (PostgreSQL, MySQL и т.д.). Вы экономите время на поиск в документации и избегаете типичных ошибок вроде пропущенного GROUP BY.

2. Объяснение сложного запроса

Задача: Понять, что делает чужой (или ваш собственный, но забытый) SQL-код.

Промт:

Объясни, что делает этот SQL-запрос (MySQL). Разбей по шагам и поясни логику:

SELECT 
    DATE_FORMAT(o.order_date, '%Y-%m') AS month,
    SUM(oi.quantity * oi.price) AS revenue
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
WHERE o.status = 'completed'
GROUP BY month
ORDER BY month;

Пример результата:

Этот запрос считает ежемесячную выручку по завершённым заказам. 
1. Соединяет таблицы orders и order_items по идентификатору заказа.
2. Фильтрует только завершённые заказы.
3. Группирует по месяцу (формат YYYY-MM).
4. Суммирует произведение количества и цены для каждой позиции.
5. Сортирует по месяцу.

Почему это работает: Промт заставляет модель декомпозировать запрос, что полезно для обучения и быстрого входа в контекст.

3. Перевод запроса из одного диалекта в другой

Задача: Перенести запрос из MySQL в PostgreSQL (или наоборот) без ручного переписывания.

Промт:

Перепиши этот MySQL-запрос для PostgreSQL, учитывая различия в синтаксисе (например, LIMIT вместо LIMIT, ILIKE вместо LIKE, DATE_FORMAT вместо TO_CHAR):

SELECT * FROM users WHERE email LIKE '%@gmail.com' LIMIT 10;

Пример результата:

SELECT * FROM users WHERE email ILIKE '%@gmail.com' LIMIT 10;

Почему это работает: LLM знает особенности диалектов и автоматически адаптирует типы данных, функции и операторы.

Продвинутые промты: когда нужно больше, чем SELECT

4. Оптимизация медленного запроса

Задача: Ускорить запрос, который выполняется слишком долго.

Промт:

Вот SQL-запрос для PostgreSQL, который выполняется 12 секунд. Проанализируй возможные причины медлительности и предложи оптимизации: добавление индексов, переписывание JOIN, использование оконных функций вместо подзапросов. 

SELECT 
    u.id,
    u.name,
    (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS order_count
FROM users u
WHERE u.created_at > '2024-01-01';

Пример результата:

-- Оптимизированная версия:
SELECT 
    u.id,
    u.name,
    COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON o.user_id = u.id
WHERE u.created_at > '2024-01-01'
GROUP BY u.id, u.name;

Почему это работает: Модель видит коррелированный подзапрос и заменяет его эквивалентным JOIN, что позволяет базе данных использовать индексы и избежать N+1 запросов. Также может предложить добавить индекс на orders(user_id).

5. Генерация случайных тестовых данных

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

Промт:

Сгенерируй SQL-скрипт для PostgreSQL, который создаст таблицу employees и заполнит её 100 строками случайных данных: id (серийный), name (имя), department (отдел из списка: IT, HR, Sales), salary (число от 30000 до 150000). Используй generate_series и random().

Пример результата:

CREATE TABLE employees (
    id SERIAL PRIMARY KEY,
    name TEXT,
    department TEXT,
    salary INTEGER
);

INSERT INTO employees (name, department, salary)
SELECT 
    'Employee_' || gs,
    (ARRAY['IT', 'HR', 'Sales'])[floor(random() * 3 + 1)],
    floor(random() * 120000 + 30000)::int
FROM generate_series(1, 100) AS gs;

Почему это работает: Модель знает встроенные функции генерации последовательностей и случайных чисел, поэтому может быстро создать правдоподобные данные для тестирования.

6. Работа с оконными функциями: ранжирование и скользящие средние

Задача: Вычислить скользящее среднее или ранг без сложных самоджойнов.

Промт:

Напиши запрос для PostgreSQL, который для каждого дня в таблице sales (поля: sale_date, revenue) вычисляет скользящее среднее за последние 7 дней (включая текущий). Используй оконную функцию.

Пример результата:

SELECT 
    sale_date,
    revenue,
    AVG(revenue) OVER (ORDER BY sale_date ROWS BETWEEN 6 PRECEDING AND CURRENT ROW) AS moving_avg_7d
FROM sales
ORDER BY sale_date;

Почему это работает: Оконные функции — мощный инструмент, но их синтаксис легко забыть. Промт напоминает модели, как строить рамки окна, и выдаёт готовый код.

Экспертные промты: настройка производительности и анализ планов

7. Анализ плана выполнения запроса

Задача: Понять, почему запрос медленный, используя EXPLAIN.

Промт:

Вот план выполнения PostgreSQL для запроса. Проанализируй его и укажи узкие места: где происходит Seq Scan, высокий cost, большие значения rows. Предложи конкретные действия: какие индексы создать, как переписать запрос.

EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM orders WHERE customer_id = 123;

Пример результата:

План показывает Seq Scan по таблице orders (cost=0.00..1000.00, rows=500). Это означает полное сканирование. Для ускорения создайте индекс:
CREATE INDEX idx_orders_customer_id ON orders(customer_id);
После этого запрос будет использовать Index Scan, что сократит cost до 0.01..0.02.

Почему это работает: Модель интерпретирует вывод EXPLAIN и даёт практические рекомендации, основанные на знаниях о внутренностях СУБД.

8. Поиск и устранение блокировок

Задача: Диагностировать взаимоблокировки (deadlocks) в MySQL.

Промт:

У меня в MySQL происходят взаимоблокировки при выполнении транзакций. Как найти их причину? Напиши SQL-запросы для просмотра текущих блокировок и информацию из performance_schema.

Пример результата:

-- Показать текущие транзакции и блокировки
SELECT 
    trx_id, 
    trx_state, 
    trx_started, 
    trx_query 
FROM information_schema.innodb_trx;

-- Показать блокировки
SELECT * FROM performance_schema.data_locks;

Почему это работает: Модель знает системные таблицы MySQL и предлагает стандартные методы диагностики.

9. Проектирование схемы базы данных

Задача: Спроектировать таблицы для нового приложения.

Промт:

Спроектируй схему базы данных PostgreSQL для интернет-магазина. Должны быть таблицы: products, categories, product_categories (многие-ко-многим), customers, orders, order_items. Укажи первичные и внешние ключи, типы данных, индексы для часто используемых полей (например, orders.customer_id).

Пример результата:

CREATE TABLE categories (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);

CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200) NOT NULL,
    price NUMERIC(10,2),
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE product_categories (
    product_id INT REFERENCES products(id),
    category_id INT REFERENCES categories(id),
    PRIMARY KEY (product_id, category_id)
);

CREATE TABLE customers (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    name VARCHAR(100)
);

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    customer_id INT REFERENCES customers(id),
    order_date TIMESTAMP DEFAULT NOW(),
    status VARCHAR(20)
);

CREATE INDEX idx_orders_customer_id ON orders(customer_id);

CREATE TABLE order_items (
    order_id INT REFERENCES orders(id),
    product_id INT REFERENCES products(id),
    quantity INT,
    price NUMERIC(10,2),
    PRIMARY KEY (order_id, product_id)
);

Почему это работает: Модель следует лучшим практикам нормализации и добавляет индексы на внешние ключи.

10. Оптимизация запросов с помощью подсказок (hints)

Задача: Принудительно указать СУБД способ выполнения запроса, если оптимизатор ошибается.

Промт:

В PostgreSQL оптимизатор выбирает неэффективный план для запроса с JOIN. Как использовать подсказки (pg_hint_plan) или другие методы, чтобы заставить использовать hash join? Приведи пример.

Пример результата:

-- Установите расширение: CREATE EXTENSION pg_hint_plan;
SELECT /*+ HashJoin(a b) */ *
FROM a JOIN b ON a.id = b.a_id;

Почему это работает: Модель знает о расширении pg_hint_plan и объясняет, как его применять.

11. Работа с JSON-данными в PostgreSQL

Задача: Извлечь данные из JSONB-поля.

Промт:

В PostgreSQL есть таблица events с полем data типа JSONB. Напиши запрос, который извлекает из JSONB поле user.email и считает количество событий по дням.

Пример результата:

SELECT 
    DATE_TRUNC('day', created_at) AS day,
    COUNT(*) AS event_count
FROM events
WHERE data->>'email' IS NOT NULL
GROUP BY day
ORDER BY day;

Почему это работает: Модель использует оператор ->> для извлечения текстового поля и DATE_TRUNC для группировки по дням.

12. Написание хранимой процедуры

Задача: Создать процедуру для автоматизации бизнес-логики.

Промт:

Создай хранимую процедуру в PostgreSQL, которая принимает customer_id и возвращает сумму всех заказов этого клиента. Используй PL/pgSQL.

Пример результата:

CREATE OR REPLACE FUNCTION get_customer_total(cust_id INTEGER)
RETURNS NUMERIC AS $$
DECLARE
    total NUMERIC;
BEGIN
    SELECT COALESCE(SUM(oi.quantity * oi.price), 0)
    INTO total
    FROM order_items oi
    JOIN orders o ON oi.order_id = o.id
    WHERE o.customer_id = cust_id;
    RETURN total;
END;
$$ LANGUAGE plpgsql;

Почему это работает: Модель генерирует корректный PL/pgSQL-код с обработкой NULL.

13. Анализ временных рядов с помощью date_trunc и generate_series

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

Промт:

В PostgreSQL есть таблица daily_sales (sale_date date, amount numeric). Некоторые дни отсутствуют. Напиши запрос, который выводит все дни за последние 30 дней и сумму продаж (0, если нет данных). Используй generate_series.

Пример результата:

WITH dates AS (
    SELECT generate_series(CURRENT_DATE - INTERVAL '29 days', CURRENT_DATE, '1 day')::date AS day
)
SELECT 
    d.day,
    COALESCE(SUM(s.amount), 0) AS total_sales
FROM dates d
LEFT JOIN daily_sales s ON s.sale_date = d.day
GROUP BY d.day
ORDER BY d.day;

Почему это работает: Модель использует CTE и generate_series для создания непрерывного ряда дат, а LEFT JOIN для подстановки нулей.

14. Настройка пула соединений и параметров PostgreSQL

Задача: Улучшить производительность сервера БД.

Промт:

Какие основные параметры в postgresql.conf влияют на производительность? Напиши рекомендации для OLTP-нагрузки с 1000 одновременными соединениями. Укажи значения для shared_buffers, work_mem, max_connections.

Пример результата:

Для OLTP-нагрузки с 1000 соединений:
- shared_buffers: 25% от ОЗУ, но не более 8ГБ (например, 4ГБ).
- work_mem: начните с 64MB, но помните, что это на каждую сортировку.
- max_connections: 1000, но лучше использовать пулер (PgBouncer), чтобы снизить накладные расходы.
- effective_cache_size: 75% от ОЗУ.
- wal_buffers: 16MB.

Почему это работает: Модель даёт проверенные рекомендации, основанные на официальной документации PostgreSQL.

15. Анализ лога медленных запросов

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

Промт:

В MySQL включён slow_query_log. Как проанализировать этот лог, чтобы найти самые медленные запросы? Напиши команды для mysqldumpslow и примеры.

Пример результата:

Используйте утилиту mysqldumpslow:

mysqldumpslow -s t -t 10 /var/lib/mysql/slow.log

Это выведет 10 самых медленных запросов по времени выполнения. Утилита агрегирует похожие запросы, заменяя параметры на N.

Почему это работает: Модель знает стандартные инструменты MySQL и их синтаксис.

Заключение

Эти 15 промтов — лишь вершина айсберга. Главное — понимать, что LLM — это не замена знаниям SQL, а мощный инструмент, который ускоряет рутину и помогает исследовать незнакомые области. Используйте их как отправную точку: адаптируйте под свои задачи, проверяйте результаты на реальных данных и не забывайте про EXPLAIN. Если вы хотите глубже освоить SQL и научиться писать эффективные запросы, обратите внимание на наш курс по анализу данных — там мы разбираем такие примеры в деталях. А пока — попробуйте применить хотя бы один промт из этой статьи в своей работе, и вы увидите, как экономите часы. Удачных запросов!

← Все статьи

Комментарии