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

URL: https://agentvera.dev/ru/blog/write-sql-with-ai/ · Опубликовано: 2026-10-27

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

**Коротко:**

- ИИ-агенты пишут хороший SQL, когда видят настоящую схему, план запроса и размеры таблиц. Дайте им пользователя базы только для чтения, а не свои админские учётные данные.
- Просите миграции маленькими обратимыми шагами (расширить, перенести данные, сузить), стройте индексы без блокировок и сначала прогоняйте каждую миграцию на копии данных.
- У MCP-серверов баз данных, например у Supabase, есть режим только для чтения. Никогда не давайте агенту запись в продакшен: пусть он пишет SQL, вы проверяете и применяете сами.

**Чтобы безопасно писать 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` записывает таблицы, столбцы, индексы и ограничения без данных:

```bash
pg_dump --schema-only --no-owner "$DEV_DATABASE_URL" > schema.sql
```

- **Подключение к базе только для чтения** через MCP-сервер, чтобы агент сам мог смотреть таблицы и столбцы (подробнее ниже).

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

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

```text
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` делает это коротким:

```sql
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` для всего, что затрагивает большую таблицу, и объяснять вам план:

```sql
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 обошёлся бы дешевле.

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

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

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

```text
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.
```

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

```sql
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, добавление столбца с константным значением по умолчанию не перезаписывает таблицу, так что это обычно дёшево. Также задайте тайм-аут блокировки в начале миграции, чтобы она быстро падала, а не вставала в очередь за долгим запросом, блокируя всё, что идёт следом:

```sql
SET lock_timeout = '5s';
```

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

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

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

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

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

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

| ORM | Сгенерировать миграцию | Применить | Посмотреть SQL |
| --- | --- | --- | --- |
| Prisma | `npx prisma migrate dev --name add_invoice --create-only` | `npx prisma migrate deploy` | созданный файл `migration.sql` |
| Drizzle | `npx drizzle-kit generate` | `npx drizzle-kit migrate` | SQL-файлы в папке миграций |
| Django | `python manage.py makemigrations` | `python manage.py migrate` | `python manage.py sqlmigrate app 0004` |
| Rails | `bin/rails generate migration AddFullNameToUsers` | `bin/rails db:migrate` | `db/schema.rb` и файл миграции |

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

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

```text
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](/ru/glossary/mcp/ "Что такое MCP")-сервер позволяет агенту самому обращаться к базе: смотреть таблицы, выполнять `SELECT`, читать план. Это полезно, и именно здесь кроется риск, поэтому начинайте с режима только для чтения.

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

```bash
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-серверы для программирования](/ru/blog/best-mcp-servers-for-coding/ "Лучшие MCP-серверы для программирования"). Старый эталонный Postgres-сервер из проекта MCP отправлен в архив.

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

```markdown
## 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](/ru/glossary/prompt-injection/ "Что такое prompt injection"). Неверно понятый запрос может превратиться в `DELETE` с неправильным фильтром. С пользователем только для чтения худший случай — медленный запрос. С правом записи — сбой или потерянные данные.

Безопасное разделение: агент пишет и проверяет SQL на данных для разработки, вы его просматриваете, а в продакшен его применяет инструмент миграций или человек. Песочницы и разрешения в целом разобраны в статье [безопасность ИИ-агентов для программирования](/ru/blog/ai-coding-agent-security/ "Безопасность ИИ-агентов для программирования").

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

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

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

[Скачайте AgentVera](/ru/download/ "Скачать AgentVera") и попробуйте на базе для разработки.

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

Дайте агенту настоящую схему и пользователя только для чтения на данных для разработки. Просите его запускать `EXPLAIN` и объяснять план, обосновывать каждый индекс и писать миграции маленькими обратимыми шагами по схеме «расширить и сузить» с неблокирующими DDL. Пусть ORM генерирует миграции, а агент их проверяет; прогоняйте каждую на копии и оставьте запись в продакшен в своих руках. Чёткие инструкции облегчают всё это; статья [как писать промпты для ИИ-агентов программирования](/ru/blog/prompts-for-ai-coding-agents/ "Промпты для ИИ-агентов программирования") подробнее разбирает контекст, планы и критерии готовности.

## Частые вопросы

**Может ли ИИ писать правильный 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.** AgentVera — настольное приложение для macOS, Windows и Linux от Solviera Teknoloji. Оно запускает 18 ИИ-агентов для программирования (Claude Code, Codex, Gemini CLI, Copilot, Cursor, Antigravity, opencode, Hermes, Qwen Code, Kimi Code, Kiro, Amp, Droid, Goose, Vibe, Crush, Aider, Cline) рядом, по проектам, в настоящих терминалах с собственными установками CLI и входами пользователя. В бесплатном тарифе количество агентов не ограничено. Скачать: https://agentvera.dev/ru/download/ · Цены: https://agentvera.dev/ru/pricing/
