Uso de IA en el trabajo

Comprobar una consulta SQL escrita por IA frente a resultados de SQLite calculados a mano

Trata una consulta SQL generada por IA como un borrador y pruébala en una pequeña base de datos SQLite con respuestas conocidas. Compara el resultado de la IA con una tabla esperada calculada manualmente antes de usar la consulta con datos reales.

Ver el índice

La traducción se ha realizado con IA. Comprueba el código, las unidades y los valores junto con el original. La revisión por hablantes nativos de cada idioma aún no se ha completado. English

Para quién esEsta guía es para personas que usan IA para redactar SQL y quieren una prueba pequeña y reproducible antes de ejecutar la consulta sobre datos importantes.

Preparación
  • Python 3.12 y un comando de terminal que inicie esa versión.
  • Una carpeta de trabajo donde los scripts puedan crear carpetas dentro de outputs.
  • Conocimientos básicos de SELECT, JOIN, GROUP BY y funciones de agregación.
  • Solo se necesita la biblioteca estándar de Python: csv, pathlib y sqlite3.

01Define la pregunta de negocio antes de revisar el SQL

Supongamos que el requisito es: para cada cliente, informar el valor total de los pedidos PAID realizados durante agosto de 2026. Los clientes sin pedidos que cumplan las condiciones deben seguir apareciendo con un total de 0.

Un asistente de IA propone un INNER JOIN entre customers y orders, seguido de condiciones WHERE para status y date. La consulta parece razonable, pero el INNER JOIN elimina a los clientes que no tienen filas de pedido coincidentes. Esto incumple el requisito de incluir a los clientes con total cero.

02Crea una base de datos SQLite sintética

La siguiente base de datos es sintética y se creó específicamente para este artículo. Tiene tres clientes y seis pedidos seleccionados para probar estados pagados y no pagados, límites entre meses y un cliente sin pedidos PAID en agosto.

python
import sqlite3
from pathlib import Path

SOURCE_DIR = Path("outputs") / "ai_sql_demo"
DATABASE = SOURCE_DIR / "orders.sqlite"

CUSTOMERS = [
    (1, "Ana"),
    (2, "Ben"),
    (3, "Cara"),
]

ORDERS = [
    (101, 1, "2026-08-03", "PAID", 40),
    (102, 1, "2026-08-20", "PAID", 60),
    (103, 1, "2026-08-25", "CANCELLED", 30),
    (104, 2, "2026-08-10", "PAID", 25),
    (105, 2, "2026-09-01", "PAID", 50),
    (106, 3, "2026-08-12", "CANCELLED", 80),
]

SOURCE_DIR.parent.mkdir(parents=True, exist_ok=True)
SOURCE_DIR.mkdir()  # Stop if the synthetic source already exists.

connection = sqlite3.connect(DATABASE)
try:
    connection.executescript("""
        CREATE TABLE customers (
            customer_id INTEGER PRIMARY KEY,
            customer_name TEXT NOT NULL
        );

        CREATE TABLE orders (
            order_id INTEGER PRIMARY KEY,
            customer_id INTEGER NOT NULL,
            order_date TEXT NOT NULL,
            status TEXT NOT NULL,
            amount INTEGER NOT NULL,
            FOREIGN KEY (customer_id) REFERENCES customers(customer_id)
        );
    """)
    connection.executemany(
        "INSERT INTO customers VALUES (?, ?)", CUSTOMERS
    )
    connection.executemany(
        "INSERT INTO orders VALUES (?, ?, ?, ?, ?)", ORDERS
    )
    connection.commit()
finally:
    connection.close()
text
python create_ai_sql_demo.py

03Calcula manualmente la respuesta correcta

Ana tiene dos pedidos PAID en agosto: 40 y 60, por lo que su total es 100. Su pedido CANCELLED no cuenta. Ben tiene un pedido válido de agosto por valor de 25; su pedido de septiembre no cuenta. Cara solo tiene un pedido CANCELLED en agosto, por lo que debe seguir apareciendo con 0.

customer_idcustomer_nameTotal PAID esperado de agosto
1Ana100
2Ben25
3Cara0

El total general esperado es 125. Debe haber exactamente 3 filas de salida porque el requisito indica que todos los clientes deben aparecer.

04Compara la consulta de IA con una consulta revisada

La consulta de IA siguiente filtra los pedidos válidos en la cláusula WHERE después de un INNER JOIN. Cara no tiene ningún pedido PAID de agosto que coincida, por lo que desaparece por completo.

sql
SELECT
    c.customer_id,
    c.customer_name,
    SUM(o.amount) AS august_paid_total
FROM customers AS c
JOIN orders AS o
    ON o.customer_id = c.customer_id
WHERE o.status = 'PAID'
  AND o.order_date >= '2026-08-01'
  AND o.order_date < '2026-09-01'
GROUP BY c.customer_id, c.customer_name
ORDER BY c.customer_id;

La consulta revisada parte de todos los clientes y utiliza LEFT JOIN. Los filtros de pedidos se colocan dentro de la condición ON para conservar a los clientes sin coincidencias. COALESCE convierte el agregado NULL resultante en 0.

sql
SELECT
    c.customer_id,
    c.customer_name,
    COALESCE(SUM(o.amount), 0) AS august_paid_total
FROM customers AS c
LEFT JOIN orders AS o
    ON o.customer_id = c.customer_id
   AND o.status = 'PAID'
   AND o.order_date >= '2026-08-01'
   AND o.order_date < '2026-09-01'
GROUP BY c.customer_id, c.customer_name
ORDER BY c.customer_id;

05Ejecuta ambas consultas y compáralas con las filas esperadas

Guarda el siguiente script como ai_sql_check.py. Ejecuta ambas consultas, compara sus resultados con la tabla esperada calculada a mano y escribe un informe CSV de revisión. No modifica la base de datos sintética.

python
import csv
import sqlite3
from pathlib import Path

DATABASE = Path("outputs") / "ai_sql_demo" / "orders.sqlite"
OUTPUT_DIR = Path("outputs") / "ai_sql_check_result"
REPORT = OUTPUT_DIR / "sql_check.csv"

AI_SQL = """
SELECT c.customer_id, c.customer_name, SUM(o.amount)
FROM customers AS c
JOIN orders AS o ON o.customer_id = c.customer_id
WHERE o.status = 'PAID'
  AND o.order_date >= '2026-08-01'
  AND o.order_date < '2026-09-01'
GROUP BY c.customer_id, c.customer_name
ORDER BY c.customer_id;
"""

REVISED_SQL = """
SELECT c.customer_id, c.customer_name, COALESCE(SUM(o.amount), 0)
FROM customers AS c
LEFT JOIN orders AS o
    ON o.customer_id = c.customer_id
   AND o.status = 'PAID'
   AND o.order_date >= '2026-08-01'
   AND o.order_date < '2026-09-01'
GROUP BY c.customer_id, c.customer_name
ORDER BY c.customer_id;
"""

EXPECTED = [
    (1, "Ana", 100),
    (2, "Ben", 25),
    (3, "Cara", 0),
]


def run_query(connection, sql):
    return connection.execute(sql).fetchall()


def main() -> None:
    if not DATABASE.is_file():
        raise FileNotFoundError(f"Database not found: {DATABASE}")
    if OUTPUT_DIR.exists():
        raise FileExistsError(f"Output folder already exists: {OUTPUT_DIR}")

    connection = sqlite3.connect(f"file:{DATABASE.resolve()}?mode=ro", uri=True)
    try:
        ai_rows = run_query(connection, AI_SQL)
        revised_rows = run_query(connection, REVISED_SQL)
    finally:
        connection.close()

    OUTPUT_DIR.parent.mkdir(parents=True, exist_ok=True)
    OUTPUT_DIR.mkdir()

    with REPORT.open("x", encoding="utf-8", newline="") as stream:
        writer = csv.writer(stream)
        writer.writerow(["query", "rows", "matches_expected"])
        writer.writerow(["AI", repr(ai_rows), ai_rows == EXPECTED])
        writer.writerow(["REVISED", repr(revised_rows), revised_rows == EXPECTED])

    print(f"Expected rows: {len(EXPECTED)}.")
    print(f"AI rows: {len(ai_rows)}; matches expected: {ai_rows == EXPECTED}.")
    print(
        f"Revised rows: {len(revised_rows)}; "
        f"matches expected: {revised_rows == EXPECTED}."
    )
    print(f"Report: {REPORT.as_posix()}")

    if revised_rows != EXPECTED:
        raise RuntimeError("Revised SQL does not match the expected result.")


if __name__ == "__main__":
    main()
text
python ai_sql_check.py

06Comprueba la salida esperada

La consulta de IA debe devolver solo a Ana y Ben, por lo que produce 2 filas y no coincide con el resultado esperado. La consulta revisada debe devolver a los 3 clientes y coincidir exactamente con la tabla calculada a mano.

ConsultaFilas devueltasTotal general esperadoCoincide con la tabla esperada
IA2125No
Revisada3125

El resultado de la IA puede seguir teniendo el total general correcto de 125 aunque sea incorrecto, porque falta la fila obligatoria de Cara con valor cero. Por eso no basta con comprobar solo los totales.

text
Expected rows: 3.
AI rows: 2; matches expected: False.
Revised rows: 3; matches expected: True.
Report: outputs/ai_sql_check_result/sql_check.csv

07Añade comprobaciones y entiende los límites

  • Comprueba el número de filas además de los totales.
  • Incluye en los datos sintéticos clientes con cero registros coincidentes cuando el requisito indique que deben aparecer.
  • Prueba límites de fecha como August 31 y September 1.
  • Incluye estados excluidos como CANCELLED.
  • Vuelve a ejecutar el comprobador sin cambiar OUTPUT_DIR. Debe detenerse con FileExistsError en lugar de sobrescribir el informe.
Problema comúnQué revisar
Faltan filas de forma inesperadaComprueba el tipo de JOIN y si los filtros en WHERE eliminan filas no coincidentes de un LEFT JOIN.
Totales demasiado altosBusca joins uno-a-muchos que dupliquen filas antes de la agregación.
Intervalo de fechas incorrectoUsa un límite inferior explícito y un límite superior exclusivo adecuado al formato de fecha almacenado.
NULL en lugar de ceroDecide si las coincidencias ausentes deben quedarse como NULL o convertirse con COALESCE.
Total correcto pero detalle incorrectoCompara todas las filas esperadas, no solo el total general.

Esta prueba solo demuestra que la consulta revisada coincide con este pequeño ejemplo sintético. Los esquemas reales pueden contener relaciones duplicadas, valores NULL, timestamps, time zones, refunds o reglas de negocio adicionales. Amplía los datos de prueba cuando esas condiciones sean importantes.

Registro de ejecución y verificación

2026-09-20 · ejemplo revisado manualmente · objetivo: Python 3.12 · biblioteca estándar: csv, pathlib, sqlite3 · sin ejecución

  • Se calculó manualmente el total válido de Ana en agosto como 40 + 60 = 100.
  • Se calculó manualmente el total válido de Ben en agosto como 25 y se excluyó su pedido de septiembre.
  • Se determinó manualmente que Cara debe aparecer con 0 porque su único pedido de agosto es CANCELLED.
  • Se calculó manualmente el total general esperado como 125 y el número esperado de filas como 3.
  • Se revisó la consulta de IA y se determinó que su INNER JOIN y sus filtros WHERE omiten a Cara, produciendo 2 filas.
  • Se revisó la consulta LEFT JOIN corregida y se esperaba que devolviera las tres filas calculadas a mano.
  • Se obtuvieron manualmente la salida de consola esperada y la comparación del informe.
Límites de la verificación
  • El autor de esta respuesta no ejecutó el código; el comportamiento de SQLite y del sistema de archivos no se probó aquí.
  • Solo se evaluaron las filas sintéticas indicadas; no se probaron importes NULL, joins duplicados, timestamps, time zones, refunds ni conjuntos de datos grandes.
  • Coincidir con este ejemplo no demuestra que la consulta sea correcta para todos los esquemas de producción o reglas de negocio.
  • Las URL de la documentación oficial se proporcionaron desde ubicaciones conocidas, pero no se comprobaron en tiempo real.

Criterios de redacción y verificación de todo el sitio

Fuentes de referencia

Las explicaciones y los ejemplos son de elaboración propia. Puedes consultar los comportamientos y conceptos relacionados en las siguientes fuentes oficiales.