Para escribir SQL con IA de forma segura, dale al agente tu esquema real y un usuario de base de datos de solo lectura, haz que te muestre el plan de la consulta y mantén cada migración pequeña, reversible y probada en una copia de los datos antes de que se acerque a producción. Un agente de programación con IA escribe consultas y migraciones muy rápido. El riesgo no es que de vez en cuando escriba mal SQL, sino que una sentencia mala se ejecute donde puede hacer daño.
Esta guía cubre cómo pedir consultas, cómo comprobarlas con EXPLAIN, cómo pedir índices, cómo escribir migraciones que no bloqueen ni pierdan datos, qué papel tienen los ORM y cómo conectar un agente a una base de datos mediante MCP sin darle las llaves de producción. Los ejemplos usan PostgreSQL; las ideas sirven también para MySQL y SQLite.
Empieza por el esquema, no por la pregunta
El motivo más común de que un agente escriba una consulta errónea es que adivinó: una columna llamada user_id que en realidad es account_id, un estado guardado como entero, un indicador de borrado lógico que no conocía. Dale primero el esquema.
De dónde sacarlo:
- Archivos de esquema del ORM.
prisma/schema.prisma, tus archivos de esquema de Drizzle,models.pyde Django,db/schema.rbde Rails. El agente puede leerlos directamente; menciónalos en el prompt. - Un volcado solo de la estructura.
pg_dump --schema-onlyescribe las tablas, columnas, índices y restricciones sin ningún dato:
pg_dump --schema-only --no-owner "$DEV_DATABASE_URL" > schema.sql
- Una conexión de solo lectura a la base de datos mediante un servidor MCP, para que el agente pueda listar tablas e inspeccionar columnas por su cuenta (más sobre esto abajo).
Luego añade lo que un esquema no dice: el tamaño aproximado de las tablas, qué columnas suelen ser NULL y qué significan los términos de negocio. “Un cliente activo es el que tiene un pedido en los últimos 90 días” evita una suposición errónea.
Un prompt para una consulta:
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.
Usa un usuario de solo lectura para el agente
Antes de que un agente ejecute nada contra una base de datos, dale su propio usuario que no pueda cambiar datos. En PostgreSQL 14 y posteriores, el rol integrado pg_read_all_data lo deja en pocas líneas:
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 concede acceso de lectura a todas las tablas, sin permisos de escritura. Las dos líneas ALTER ROLE añaden una protección extra: las transacciones empiezan en solo lectura y una consulta descontrolada se cancela a los 30 segundos. En versiones anteriores, o si quieres un acceso más restringido, concede en su lugar CONNECT, USAGE sobre el esquema y SELECT sobre las tablas que quieras.
Apunta este usuario a una base de datos de desarrollo o staging con datos realistas, no a producción. Si necesitas cifras de producción, usa una réplica de lectura o una copia anonimizada reciente.
Lee el plan de la consulta
Una consulta que devuelve las filas correctas puede tardar minutos. Pide al agente que ejecute EXPLAIN en todo lo que toque una tabla grande y que te explique el plan:
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 a secas muestra el plan que usaría la base de datos. ANALYZE ejecuta de verdad la consulta y añade tiempos y recuentos de filas reales. Esto importa: EXPLAIN ANALYZE sobre un UPDATE o un DELETE cambia datos de verdad. Envuélvelos en una transacción que deshagas con rollback, o ejecútalos solo en una copia.
Qué pedirle al agente que busque:
- Escaneos secuenciales en tablas grandes cuando filtras unas pocas filas.
- Filas estimadas y reales muy distintas, lo que apunta a estadísticas desactualizadas (
ANALYZE tablenamelas refresca). - Ordenaciones o hashes que se vuelcan a disco.
- Bucles anidados sobre muchas filas donde un hash join saldría más barato.
Un buen prompt de seguimiento: “Explícame este plan con palabras sencillas. ¿Qué paso tarda más y por qué? Propón un cambio y muéstrame el nuevo plan.”
Pide índices, con sus contrapartidas
A los agentes les gusta sugerir índices. Cada uno acelera algunas lecturas, ralentiza todas las escrituras en esa tabla y ocupa disco. Pide el razonamiento:
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.
En una tabla con mucho tráfico, crea el índice sin bloquear las escrituras:
CREATE INDEX CONCURRENTLY idx_orders_customer_created
ON orders (customer_id, created_at);
CONCURRENTLY tarda más, pero no bloquea inserciones ni actualizaciones mientras se ejecuta. No puede ejecutarse dentro de un bloque de transacción, y eso importa porque muchas herramientas de migración envuelven cada migración en una transacción; comprueba cómo lo gestiona la tuya (la mayoría tiene una opción para desactivar la transacción en una migración concreta). Si una creación concurrente falla, deja atrás un índice inválido que tendrás que borrar y volver a crear.
Escribe migraciones seguras de ejecutar
Una migración se ejecuta sobre datos vivos, a menudo mientras la app atiende tráfico. Estos son los patrones que debes pedirle a tu agente y comprobar en lo que entrega.
Expandir, migrar, contraer
No renombres ni borres en un solo paso. Para renombrar users.name a users.full_name:
- Expandir: añade
full_namecomo columna que admite NULL. Despliega código que escriba en ambas columnas. - Migrar: rellena
full_namea partir denamepor lotes. Cambia las lecturas afull_name. - Contraer: en una release posterior, cuando nada lea ya
name, bórrala.
Cada paso se puede desplegar y revertir por separado. Un único ALTER TABLE ... RENAME COLUMN rompe todas las instancias en marcha del código antiguo en el momento en que se ejecuta.
Evita bloqueos largos
Algunas sentencias DDL toman un bloqueo que impide lecturas o escrituras mientras se ejecutan. Pide al agente que las señale y use la forma más segura:
| Arriesgado | Más seguro |
|---|---|
CREATE INDEX en una tabla grande | CREATE INDEX CONCURRENTLY |
| Añadir una clave foránea en un solo paso | ADD CONSTRAINT ... NOT VALID y luego VALIDATE CONSTRAINT en un paso aparte |
SET NOT NULL en una tabla grande | Añade un CHECK (col IS NOT NULL) NOT VALID, valídalo y luego pon NOT NULL |
| Cambiar el tipo de una columna | Añade una columna nueva, rellénala, cambia a ella y borra la antigua |
Un único UPDATE enorme para rellenar datos | Lotes de unos pocos miles de filas |
Desde PostgreSQL 11, añadir una columna con un valor por defecto constante no reescribe la tabla, así que eso suele ser barato. Pon además un tiempo máximo de espera de bloqueo al principio de la migración, para que falle rápido en lugar de quedarse en cola detrás de una consulta larga y bloquear todo lo que venga detrás:
SET lock_timeout = '5s';
Hazla reversible
Toda migración debería tener un camino de vuelta, ya sea una migración down o un paso de rollback por escrito. Pídelo explícitamente: “Escribe la migración up y la down, y dime qué pasos no se pueden deshacer sin perder datos.” Borrar una columna no se revierte con una migración down; los datos ya no están. Es otro motivo para borrar en un paso posterior y separado, después de una copia de seguridad.
Pruébala en una copia
Ejecuta la migración en una copia de los datos antes de producción: una base de datos de staging restaurada desde una copia de seguridad reciente, o una base de datos local con datos falsos del tamaño de producción. Comprueba tres cosas: que termina, cuánto tarda y que la app sigue funcionando con el nuevo esquema. Luego ejecuta la migración down y compruébala también.
Con un ORM: deja que genere y que el agente revise
Si usas un ORM, el flujo más seguro es: cambia el modelo, deja que el ORM genere la migración y haz que el agente revise el SQL generado.
| ORM | Generar una migración | Aplicar | Ver el SQL |
|---|---|---|---|
| Prisma | npx prisma migrate dev --name add_invoice --create-only | npx prisma migrate deploy | el archivo migration.sql que crea |
| Drizzle | npx drizzle-kit generate | npx drizzle-kit migrate | los archivos SQL de tu carpeta de migraciones |
| 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 y el archivo de la migración |
El --create-only de Prisma escribe la migración sin aplicarla, así que puedes editar antes el SQL, por ejemplo para añadir CONCURRENTLY o dividir un paso. Drizzle también tiene drizzle-kit push, que aplica los cambios de esquema directamente sin archivos de migración; es práctico para prototipar en local, pero usa migraciones generadas para todo lo compartido.
Un prompt de revisión que funciona con cualquiera de ellos:
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.
Conectar un agente a una base de datos con MCP
Un servidor MCP permite al agente consultar la base de datos por su cuenta: listar tablas, ejecutar un SELECT, leer un plan. Es útil, y también es donde está el riesgo, así que empieza en solo lectura.
Supabase ofrece un servidor MCP alojado. A octubre de 2026, añadir read_only=true a su URL ejecuta todas las consultas como un usuario de Postgres de solo lectura, y project_ref lo limita a un proyecto:
claude mcp add --transport http supabase \
"https://mcp.supabase.com/mcp?project_ref=<your-project-ref>&read_only=true"
La propia recomendación de Supabase es conectar un proyecto de producción solo cuando la tarea necesite de verdad datos de producción, y en ese caso con el alcance limitado al proyecto y el modo de solo lectura activado; mantener la aprobación manual de las llamadas a herramientas; y revisar los resultados antes de actuar, porque las protecciones contra la inyección de prompts no son infalibles.
PostgreSQL sin más: usa un servidor MCP de Postgres mantenido con tu usuario agent_ro en la cadena de conexión. Los mejores servidores MCP para programar enumera opciones y sus ajustes de solo lectura. El antiguo servidor Postgres de referencia del proyecto MCP está archivado.
Las reglas que vale la pena escribir en tu CLAUDE.md o 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.
Nunca des a un agente acceso de escritura a producción
Esta es la única regla que no se dobla. Las filas de datos pueden contener texto que parece una instrucción (un ticket de soporte, la bio de un usuario), y un agente que las lea puede seguirlas; eso es inyección de prompts. Una petición malentendida puede convertirse en un DELETE con el filtro equivocado. Con un usuario de solo lectura, lo peor que puede pasar es una consulta lenta. Con acceso de escritura, es una caída o datos perdidos.
El reparto seguro: el agente escribe y prueba el SQL con datos de desarrollo; tú lo revisas; una herramienta de migración o una persona lo aplica en producción. Seguridad de los agentes de programación con IA trata el sandboxing y los permisos de forma más amplia.
Cómo hacerlo en AgentVera
AgentVera es una app de escritorio para macOS, Windows y Linux que ejecuta Claude Code, Codex y otros agentes CLI uno al lado de otro. Algunas funciones están hechas justo para este trabajo:
- Gestor de bases de datos: conexiones a PostgreSQL, MySQL/MariaDB y SQLite en la misma ventana que tus agentes, con una vista de tabla editable, un editor SQL con
EXPLAIN, un diseñador de tablas que muestra el DDL antes de aplicarlo y un diagrama ER. Las conexiones marcadas como de solo lectura también se abren en solo lectura en el lado del servidor, y una conexión marcada como producción se muestra en rojo y pide confirmación antes de aplicar cambios. Puedes dejar que los agentes usen una conexión mediante la herramienta integrada agentvera-db: el acceso está desactivado por defecto, empieza en modo de solo lectura cuando lo activas, los agentes nunca ven la contraseña y cada consulta que ejecutan queda registrada. El gestor de bases de datos está en los planes Pro y Team. - DB Guard: cuando un agente termina un turno, los archivos de migración modificados se analizan en busca de riesgos como
DROP,TRUNCATE,UPDATEoDELETEsinWHERE,CREATE INDEXsinCONCURRENTLY, restricciones nuevas y pasos down que faltan. Con una conexión similar a producción seleccionada, el riesgo se ajusta según el tamaño real de las tablas. Puedes probar la migración en una base de datos sombra dentro de una transacción que siempre se deshace, o devolver los hallazgos al agente para que los corrija. - Gestor de MCP: configura un servidor MCP de base de datos una vez y se conecta a tus agentes en cada arranque.
- Revisión de código: un segundo modelo revisa el código de migraciones y consultas que escribió el agente cuando termina su turno.
Descarga AgentVera y pruébalo con una base de datos de desarrollo.
Para terminar
Dale al agente el esquema real y un usuario de solo lectura sobre datos de desarrollo. Pídele que ejecute EXPLAIN y explique el plan, que justifique cada índice y que escriba las migraciones en pasos pequeños y reversibles de expandir y contraer, con DDL que no bloquee. Deja que tu ORM genere las migraciones y que el agente las revise, prueba cada una en una copia y guarda para ti las escrituras en producción. Unas instrucciones claras lo facilitan todo; cómo escribir prompts para agentes de programación con IA trata con más detalle el contexto, los planes y los criterios de terminado.