Как писать SQL и миграции с помощью ИИ

Как безопасно писать SQL и миграции с помощью ИИ-агентов: дайте схему, отдельного пользователя только для чтения, проверяйте EXPLAIN, индексы и миграции на копии.

Solviera Teknoloji 9 мин чтения

Чтобы безопасно писать SQL с помощью ИИ, дайте агенту настоящую схему и пользователя базы только для чтения, заставьте его показывать план запроса, а каждую миграцию делайте маленькой, обратимой и проверенной на копии данных, прежде чем она окажется рядом с продакшеном. ИИ-агент для программирования быстро пишет запросы и миграции. Риск не в том, что иногда он пишет плохой SQL, а в том, что плохой запрос выполнится там, где может навредить.

В этом руководстве разобрано, как формулировать промпты для запросов, как проверять их через EXPLAIN, как просить индексы, как писать миграции, которые не блокируют таблицы и не теряют данные, какое место занимают ORM и как подключить агента к базе через MCP, не отдавая ему ключи от продакшена. Примеры написаны для PostgreSQL; идеи переносятся на MySQL и SQLite.

Начните со схемы, а не с вопроса

Чаще всего агент пишет неверный запрос потому, что угадывал: столбец user_id, который на самом деле называется account_id, статус, хранящийся числом, флаг мягкого удаления, о котором он не знал. Сначала дайте ему схему.

Где её взять:

  • Файлы схемы ORM. prisma/schema.prisma, файлы схемы Drizzle, models.py в Django, db/schema.rb в Rails. Агент может прочитать их напрямую; просто укажите их в промпте.
  • Выгрузка только структуры. pg_dump --schema-only записывает таблицы, столбцы, индексы и ограничения без данных:
pg_dump --schema-only --no-owner "$DEV_DATABASE_URL" > schema.sql
  • Подключение к базе только для чтения через MCP-сервер, чтобы агент сам мог смотреть таблицы и столбцы (подробнее ниже).

Затем добавьте то, чего схема не говорит: примерные размеры таблиц, какие столбцы часто бывают NULL и что означают бизнес-термины. «Активный клиент — тот, у кого был заказ за последние 90 дней» избавляет от неверной догадки.

Промпт для запроса:

Using schema.sql: write a PostgreSQL query that returns, for each country,
the number of active customers (an order in the last 90 days) and their
total order value this year. Exclude soft-deleted customers
(deleted_at IS NOT NULL). orders has about 20M rows, customers 400k.
Run it on the dev database, show me the first 10 rows and the EXPLAIN plan.

Дайте агенту пользователя только для чтения

Прежде чем агент что-либо выполнит в базе, создайте ему отдельного пользователя, который не может менять данные. В PostgreSQL 14 и новее встроенная роль pg_read_all_data делает это коротким:

CREATE ROLE agent_ro LOGIN PASSWORD 'change-me';
GRANT pg_read_all_data TO agent_ro;
ALTER ROLE agent_ro SET default_transaction_read_only = on;
ALTER ROLE agent_ro SET statement_timeout = '30s';

pg_read_all_data даёт доступ на чтение ко всем таблицам без права записи. Две строки ALTER ROLE добавляют страховку: транзакции начинаются в режиме только для чтения, а вышедший из-под контроля запрос отменяется через 30 секунд. В старых версиях или если нужен более узкий доступ выдайте вместо этого CONNECT, USAGE на схему и SELECT на нужные таблицы.

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

Читайте план запроса

Запрос, который возвращает правильные строки, всё равно может выполняться минутами. Просите агента запускать EXPLAIN для всего, что затрагивает большую таблицу, и объяснять вам план:

EXPLAIN (ANALYZE, BUFFERS)
SELECT c.country, count(DISTINCT c.id)
FROM customers c
JOIN orders o ON o.customer_id = c.id
WHERE o.created_at > now() - interval '90 days'
  AND c.deleted_at IS NULL
GROUP BY c.country;

Сам по себе EXPLAIN показывает план, который использовала бы база. ANALYZE действительно выполняет запрос и добавляет реальное время и число строк. Это важно: EXPLAIN ANALYZE для UPDATE или DELETE по-настоящему меняет данные. Оборачивайте такие запросы в транзакцию, которую откатываете, или запускайте их только на копии.

На что попросить агента обратить внимание:

  • Последовательное сканирование больших таблиц, когда фильтр отбирает несколько строк.
  • Сильное расхождение оценки и фактического числа строк — признак устаревшей статистики (ANALYZE tablename её обновляет).
  • Сортировки или хеши, которые сбрасываются на диск.
  • Вложенные циклы по множеству строк, где hash join обошёлся бы дешевле.

Хороший следующий промпт: «Объясни этот план простыми словами. Какой шаг занимает больше всего времени и почему? Предложи одно изменение и покажи новый план».

Просите индексы вместе с их ценой

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

The query above does a sequential scan on orders. Suggest at most one
index, explain which queries it helps and what it costs on writes,
and show the EXPLAIN plan with it on the dev database.

На нагруженной таблице стройте индекс, не блокируя запись:

CREATE INDEX CONCURRENTLY idx_orders_customer_created
  ON orders (customer_id, created_at);

CONCURRENTLY работает дольше, но не блокирует вставки и обновления, пока строится индекс. Его нельзя выполнять внутри блока транзакции, и это важно, потому что многие инструменты миграций оборачивают каждую миграцию в транзакцию; проверьте, как это делает ваш инструмент (у большинства есть настройка, отключающая транзакцию для одной миграции). Если параллельное построение падает, после него остаётся невалидный индекс, который нужно удалить и построить заново.

Пишите миграции, которые безопасно запускать

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

Расширить, перенести, сузить

Не переименовывайте и не удаляйте за один шаг. Чтобы переименовать users.name в users.full_name:

  1. Расширение: добавьте full_name как столбец, допускающий NULL. Выкатите код, который пишет в оба столбца.
  2. Перенос: заполните full_name из name пакетами. Переключите чтение на full_name.
  3. Сужение: в одном из следующих релизов, когда name уже никто не читает, удалите его.

Каждый шаг можно выкатить и откатить отдельно. Один ALTER TABLE ... RENAME COLUMN ломает все запущенные экземпляры старого кода в тот же момент, когда выполняется.

Избегайте долгих блокировок

Некоторые DDL-команды берут блокировку, которая не даёт читать или писать, пока они выполняются. Просите агента отмечать такие места и использовать более безопасный вариант:

РискованноБезопаснее
CREATE INDEX на большой таблицеCREATE INDEX CONCURRENTLY
Добавление внешнего ключа за один шагADD CONSTRAINT ... NOT VALID, затем VALIDATE CONSTRAINT отдельным шагом
SET NOT NULL на большой таблицеДобавьте CHECK (col IS NOT NULL) NOT VALID, проверьте его, затем установите NOT NULL
Изменение типа столбцаДобавьте новый столбец, заполните его, переключитесь, удалите старый
Один огромный UPDATE для заполненияПакеты по несколько тысяч строк

Начиная с PostgreSQL 11, добавление столбца с константным значением по умолчанию не перезаписывает таблицу, так что это обычно дёшево. Также задайте тайм-аут блокировки в начале миграции, чтобы она быстро падала, а не вставала в очередь за долгим запросом, блокируя всё, что идёт следом:

SET lock_timeout = '5s';

Делайте миграции обратимыми

У каждой миграции должен быть путь назад: down-миграция или описанный шаг отката. Просите об этом явно: «Напиши up- и down-миграцию и скажи, какие шаги нельзя отменить без потери данных». Удаление столбца down-миграцией не отменить: данных уже нет. Это ещё одна причина удалять в более позднем отдельном шаге, после резервной копии.

Проверяйте на копии

Прогоните миграцию на копии данных до продакшена: на staging-базе, восстановленной из свежей резервной копии, или на локальной базе с фейковыми данными продакшен-объёма. Проверьте три вещи: что она завершается, сколько длится и что приложение работает с новой схемой. Затем запустите down-миграцию и проверьте и её.

С ORM: пусть она генерирует, а агент проверяет

Если вы пользуетесь ORM, самый безопасный порядок такой: измените модель, дайте ORM сгенерировать миграцию и попросите агента проверить получившийся SQL.

ORMСгенерировать миграциюПрименитьПосмотреть SQL
Prismanpx prisma migrate dev --name add_invoice --create-onlynpx prisma migrate deployсозданный файл migration.sql
Drizzlenpx drizzle-kit generatenpx drizzle-kit migrateSQL-файлы в папке миграций
Djangopython manage.py makemigrationspython manage.py migratepython manage.py sqlmigrate app 0004
Railsbin/rails generate migration AddFullNameToUsersbin/rails db:migratedb/schema.rb и файл миграции

--create-only в Prisma записывает миграцию, не применяя её, так что SQL можно сначала отредактировать, например добавить CONCURRENTLY или разбить шаг. У Drizzle есть ещё drizzle-kit push, который применяет изменения схемы напрямую, без файлов миграций; это удобно для локальных прототипов, но для всего общего используйте сгенерированные миграции.

Промпт для ревью, который подходит для любой из них:

Read the new migration in prisma/migrations/. For each statement, tell me:
does it lock the table, can it lose data, how long will it take on a
table with 20M rows, and is there a safer form? Don't change any files.

Подключение агента к базе данных через MCP

MCP-сервер позволяет агенту самому обращаться к базе: смотреть таблицы, выполнять SELECT, читать план. Это полезно, и именно здесь кроется риск, поэтому начинайте с режима только для чтения.

Supabase предоставляет облачный MCP-сервер. По состоянию на октябрь 2026 года параметр read_only=true в его URL заставляет выполнять каждый запрос от имени пользователя Postgres только для чтения, а project_ref привязывает сервер к одному проекту:

claude mcp add --transport http supabase \
  "https://mcp.supabase.com/mcp?project_ref=<your-project-ref>&read_only=true"

Сама Supabase советует подключать продакшен-проект, только когда задаче действительно нужны продакшен-данные, и тогда — с привязкой к проекту и режимом только для чтения; не отключать ручное подтверждение вызовов инструментов; и проверять результаты, прежде чем на них опираться, потому что защита от prompt injection не абсолютна.

Обычный PostgreSQL: используйте поддерживаемый MCP-сервер для Postgres с пользователем agent_ro в строке подключения. Варианты и их настройки только для чтения перечислены в статье лучшие MCP-серверы для программирования. Старый эталонный Postgres-сервер из проекта MCP отправлен в архив.

Правила, которые стоит записать в CLAUDE.md или AGENTS.md:

## Database
- The database MCP server is read-only. Never ask for write access.
- Schema changes go through migrations in prisma/migrations, never direct DDL.
- Never run UPDATE or DELETE without a WHERE clause.
- Run new queries with EXPLAIN on the dev database before proposing them.

Никогда не давайте агенту запись в продакшен

Это единственное правило, которое нельзя нарушать. В строках данных может оказаться текст, похожий на инструкции (тикет поддержки, описание пользователя), и агент, читая его, может им последовать; это prompt injection. Неверно понятый запрос может превратиться в DELETE с неправильным фильтром. С пользователем только для чтения худший случай — медленный запрос. С правом записи — сбой или потерянные данные.

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

Как это делать в AgentVera

AgentVera — десктопное приложение для macOS, Windows и Linux, которое запускает Claude Code, Codex и других CLI-агентов рядом друг с другом. Несколько функций сделаны именно для такой работы:

  • Менеджер баз данных: подключения к PostgreSQL, MySQL/MariaDB и SQLite в том же окне, что и ваши агенты, с редактируемым видом таблиц, SQL-редактором с EXPLAIN, конструктором таблиц, который показывает DDL перед применением, и ER-диаграммой. Подключения, помеченные как только для чтения, открываются в режиме только для чтения и на стороне сервера, а подключение, помеченное как продакшен, выделяется красным, и изменения в нём сначала требуют подтверждения. Агентам можно разрешить пользоваться подключением через встроенный инструмент agentvera-db: по умолчанию доступ выключен, при включении он начинается в режиме только для чтения, агенты никогда не видят пароль, а каждый их запрос записывается в журнал. Менеджер баз данных доступен на тарифах Pro и Team.
  • DB Guard: когда агент заканчивает ход, изменённые файлы миграций проверяются на риски: DROP, TRUNCATE, UPDATE или DELETE без WHERE, CREATE INDEX без CONCURRENTLY, новые ограничения и отсутствующие down-шаги. Если выбрано подключение, похожее на продакшен, риск оценивается с учётом реальных размеров таблиц. Миграцию можно попробовать на теневой базе внутри транзакции, которая всегда откатывается, или отправить замечания обратно агенту на исправление.
  • Менеджер MCP: настройте MCP-сервер базы данных один раз, и он подключается к вашим агентам при каждом запуске.
  • Код-ревью: когда агент заканчивает ход, вторая модель проверяет написанный им код миграций и запросов.

Скачайте AgentVera и попробуйте на базе для разработки.

Подведём итог

Дайте агенту настоящую схему и пользователя только для чтения на данных для разработки. Просите его запускать EXPLAIN и объяснять план, обосновывать каждый индекс и писать миграции маленькими обратимыми шагами по схеме «расширить и сузить» с неблокирующими DDL. Пусть ORM генерирует миграции, а агент их проверяет; прогоняйте каждую на копии и оставьте запись в продакшен в своих руках. Чёткие инструкции облегчают всё это; статья как писать промпты для ИИ-агентов программирования подробнее разбирает контекст, планы и критерии готовности.

Вопросы

Может ли ИИ писать правильный SQL?

Часто да, если видит вашу настоящую схему и форму данных. Большинство неверных запросов возникает из-за угаданных имён столбцов, пропущенных JOIN или неожиданных NULL. Дайте агенту схему, несколько примерных строк и ожидаемый результат и попросите выполнить запрос на базе для разработки, прежде чем ему доверять.

Безопасно ли подключать агента для программирования к моей базе данных?

К базе для разработки или staging с пользователем только для чтения — да, если быть аккуратным. К продакшену с правом записи — нет. Агент может неверно понять запрос, выполнить инструкцию, внедрённую в данные, или запустить DELETE без WHERE. Все записи должны идти через миграцию, которую вы проверяете и применяете сами.

Как передать ИИ-агенту схему базы данных?

Укажите файл схемы, который ведёт ваша ORM (schema.prisma, файлы схемы Drizzle, модели Django), или выгрузите только структуру, например через pg_dump --schema-only. MCP-сервер базы данных в режиме только для чтения позволяет агенту самому смотреть таблицы и столбцы.

Кто должен писать миграцию: ИИ или ORM?

Пусть ORM сгенерирует миграцию по изменению модели, а агент проверит получившийся SQL на блокировки, потерю данных и пропущенные шаги. Сырой SQL вручную (или руками агента) пишите только для того, что ORM выразить не может, например для параллельного построения индексов или заполнения данных.

Что такое паттерн expand and contract?

Способ менять схему без простоя. Сначала расширение: добавьте новый столбец или таблицу рядом со старыми. Затем перенос: выкатите код, который пишет в оба места, и заполните старые строки. И наконец сужение: когда старую структуру уже никто не читает, удалите её в одной из следующих миграций.

Соберите своих агентов за одним столом.

Скачайте AgentVera бесплатно; ваши установленные CLI уже готовы к работе.

Другие статьи