Почему SQL-скиллы снова в моде в 2026 году
Пока индустрия спорит о том, заменит ли ИИ разработчиков, одна вещь остаётся неизменной: почти любое приложение упирается в базу данных. По данным Stack Overflow Developer Survey, PostgreSQL который год подряд удерживает статус самой используемой и самой желанной СУБД среди профессионалов. И это логично: реляционная модель никуда не делась, а вместе с ней никуда не делась и боль — медленные запросы, раздутые индексы, миграции, которые ломают прод, и ревью SQL, где никто не понимает, что делает LEFT JOIN на пять таблиц.
Хорошая новость: LLM научились быть полезными в этой рутине. Не «напиши мне SELECT» на уровне джуна, а разбирать EXPLAIN (ANALYZE, BUFFERS), предлагать композитные индексы, находить N+1 в ORM-логах и проектировать схемы с учётом нормальных форм. Плохая новость: без правильного промта вы получите галлюцинацию с несуществующими функциями.
Ниже — 12 сценариев, разбитых на три уровня: базовые, продвинутые и экспертные. Каждый проверен на реальных задачах, каждый содержит задачу, промт и пример результата. Всё, что касается синтаксиса, соответствует официальной документации PostgreSQL (postgresql.org/docs) и стандарту SQL:2023 (ISO/IEC 9075).
Базовые промты: от «что тут происходит» до первого индекса
1. Чтение плана запроса для новичка
Задача. Вы получили EXPLAIN ANALYZE и видите Seq Scan, Hash Join и cost=0.00.. — но не понимаете, что из этого проблема.
Промт.
Ты — DBA PostgreSQL 16. Вот план запроса: <вставь EXPLAIN (ANALYZE, BUFFERS)>. Объясни построчно, что делает каждый узел, где основная стоимость, и какие 3 гипотезы для оптимизации ты бы проверил первыми. Используй терминологию из официальной документации PostgreSQL, раздел «Using EXPLAIN».
Пример результата. Модель укажет, что Seq Scan on orders с rows=1 при оценке и rows=120000 фактически — это классический симптом устаревшей статистики; предложит ANALYZE orders и проверку pg_stat_user_tables.last_analyze. Это не магия, а воспроизводимое чтение дерева плана.
2. Генерация индекса под конкретный запрос
Задача. Есть тяжёлый фильтр по двум колонкам и сортировка по третьей.
Промт.
Вот запрос: <SQL>. Вот текущие индексы: <\d+ table>. Предложи минимальный набор индексов (включая порядок колонок и опции ASC/DESC, WHERE для partial index), обоснуй каждый и предупреди о влиянии на INSERT/UPDATE. Не предлагай индексы, дублирующие существующие префиксы.
Пример результата. Для WHERE tenant_id = $1 AND status = 'active' ORDER BY created_at DESC модель предложит CREATE INDEX CONCURRENTLY idx_orders_tenant_status_created ON orders (tenant_id, status, created_at DESC) WHERE status = 'active'; — с пояснением, что partial index уменьшит размер и что CONCURRENTLY не блокирует запись, но не работает внутри транзакции.
3. Перевод сырого SQL в ORM-запрос и обратно
Задача. Команда переезжает с psycopg2 на SQLAlchemy 2.0, и надо сверить поведение.
Промт.
Переведи этот SQL в SQLAlchemy 2.0 (select() синтаксис): <SQL>. Убедись, что семантика NULL, ORDER BY и LIMIT идентична. Покажи сгенерированный SQL через str(stmt.compile(compile_kwargs={'literal_binds': True})) и укажи расхождения, если они есть.
Пример результата. Модель честно отметит, что count(*) в ORM лучше писать через func.count(), а не len(result.scalars().all()), иначе получите N+1 и загрузку всех строк в память.
4. Быстрая проверка запроса на антипаттерны
Задача. Ревью перед мержем.
Промт.
Проверь SQL на антипаттерны: SELECT *, функции над индексированными колонками в WHERE, NOT IN с NULL, отсутствие LIMIT, implicit cast. Для каждой находки дай переписанный вариант.
Пример результата. WHERE date(created_at) = '2026-09-30' превращается в диапазон created_at >= '2026-09-30' AND created_at < '2026-10-01', что позволяет использовать индекс по created_at (index-only scan при покрывающем индексе).
Продвинутые промты: миграции, окна, ревью
5. Безопасная миграция без даунтайма
Задача. Добавить NOT NULL колонку в таблицу на 200 млн строк.
Промт.
Спроектируй миграцию для PostgreSQL 16: добавить колонку region text NOT NULL DEFAULT 'eu' в таблицу orders (200M строк, горячая запись). Разбей на шаги с учётом блокировок, версий PG и отката. Укажи, где нужен ALTER TABLE ... SET DEFAULT, а где backfill батчами.
Пример результата. С версии 11 PostgreSQL не переписывает таблицу при ADD COLUMN ... DEFAULT, но SET NOT NULL требует полного сканирования — модель предложит сначала ADD CONSTRAINT ... NOT VALID, затем VALIDATE CONSTRAINT, что берёт только SHARE UPDATE EXCLUSIVE.
6. Разбор N+1 из логов ORM
Задача. API отвечает 3 секунды, в логах сотни одинаковых SELECT.
Промт.
Вот фрагмент лога SQLAlchemy с 400+ запросами: <лог>. Найди N+1, предложи eager loading (selectinload/joinedload), покажи переписанный код и объясни, почему joinedload хуже для one-to-many здесь.
Пример результата. Модель укажет, что joinedload на коллекции даст декартово произведение и раздутый результат, а selectinload сделает один дополнительный IN (...) — это соответствует рекомендациям документации SQLAlchemy по загрузке связей.
7. Оконные функции вместо самосоединений
Задача. Нужен топ-1 заказ на клиента.
Промт.
Перепиши коррелированный подзапрос через оконные функции: <SQL>. Используй ROW_NUMBER() OVER (PARTITION BY ... ORDER BY ...). Сравни планы и объясни, почему оконная функция читает таблицу один раз.
Пример результата. Замена коррелированного подзапроса на ROW_NUMBER() в CTE убирает повторное сканирование и часто даёт выигрыш в разы на больших таблицах — это классический приём, описанный в документации PostgreSQL в разделе «Window Functions».
8. Ревью схемы по нормальным формам
Задача. Есть таблица с JSONB и денормализованными полями.
Промт.
Вот DDL: <CREATE TABLE ...>. Оцени, где нарушены 1НФ–3НФ, где JSONB оправдан (гибкие атрибуты), а где создаёт аномалии обновления. Предложи нормализованный вариант и миграционный путь без потери данных.
Пример результата. Модель отличит «JSONB как свалку» от «JSONB как расширяемые атрибуты товара» и предложит вынести частые фильтры в GIN-индексируемые колонки или generated columns.
Экспертные промты: производительность и архитектура
9. Диагностика блокировок и deadlock
Задача. Прод завис, в логах deadlock detected.
Промт.
Вот вывод pg_locks и pg_stat_activity: <данные>. Реконструируй граф ожиданий, найди цикл, предложи порядок захвата блокировок и как переписать транзакцию, чтобы deadlock исчез. Учти уровень изоляции READ COMMITTED.
Пример результата. Модель покажет, что две транзакции обновляют строки в разном порядке, и предложит канонический фикс: сортировать UPDATE ... WHERE id IN (...) по id, чтобы все транзакции захватывали блокировки в одном порядке.
10. Партиционирование большой таблицы
Задача. Таблица событий растёт на миллионы строк в месяц.
Промт.
Предложи стратегию декларативного партиционирования для таблицы events (append-only, 500M строк). Сравни RANGE по created_at и LIST по tenant_id. Дай DDL, объясни влияние на индексы, autovacuum и запросы с фильтром по партиционному ключу.
Пример результата. Модель напомнит, что уникальные индексы на партиционированной таблице обязаны включать ключ партиционирования, и что enable_partition_pruning (по умолчанию on) отсечёт лишние партиции только при константном или параметризованном предикате.
11. Настройка autovacuum под нагрузку
Задача. Раздувание таблиц и рост bloat.
Промт.
Таблица: 300M строк, 40% UPDATE в час. Текущие настройки autovacuum дефолтные. Предложи per-table настройки (autovacuum_vacuum_scale_factor, cost_limit и др.), объясни trade-off с I/O и как проверить эффект через pg_stat_progress_vacuum.
Пример результата. Модель предложит снизить scale_factor до 0.01 для горячей таблицы и напомнит про pg_stat_user_tables.n_dead_tup как метрику для мониторинга.
12. Проектирование схемы «с нуля» под требования
Задача. Новый сервис, надо спроектировать БД.
Промт.
Спроектируй схему PostgreSQL для сервиса бронирования (пользователи, ресурсы, слоты, платежи). Требования: <список>. Выдай DDL с комментариями, ограничениями целостности, типами и индексами. Объясни выбор типов (timestamptz vs timestamp, numeric для денег) и как обеспечить защиту от двойного бронирования на уровне БД.
Пример результата. Модель предложит exclusion constraint с btree_gist для запрета пересекающихся слотов — это реальная возможность PostgreSQL, описанная в документации (CREATE TABLE ... EXCLUDE USING gist), а деньги — numeric(12,2), а не float.
Что важно помнить
Промты — это не замена пониманию. LLM отлично перебирает гипотезы и напоминает про грабли, но финальное решение остаётся за вами: проверяйте планы через EXPLAIN (ANALYZE, BUFFERS), тестируйте миграции на копии прода, читайте официальную документацию PostgreSQL и стандарт SQL:2023. Хороший промт экономит часы рутины, но не отменяет инженерной ответственности.
Соберите эти 12 сценариев в свой рабочий чек-лист: начните с чтения планов и антипаттернов, затем перейдите к миграциям и партиционированию. Через пару недель вы заметите, что ревью SQL перестало быть болью, а вопросы к базе — превратились в понятный диалог с инструментом.
Комментарии