Publicado el 5 min de lectura

Prueba el SQL generado por IA con pequeños juegos de datos de SQLite

En esta página

El SQL generado por IA puede ejecutarse sin errores y aun así omitir a clientes que nunca hicieron pedidos. Prueba sus totales con una base de datos pequeña de respuestas conocidas.

Crea una comprobación local de corrección con Python y datos sintéticos de SQLite. No necesitas una cuenta de IA, paquetes ni conexión a producción. Estas consultas escritas a mano prueban el comprobador, no un producto de IA.

Una base de datos turquesa y tarjetas de tablas sobre dos bandejas de resultados, con una lupa que compara filas y una discrepancia ámbar apartada.

1. Define el informe antes de pedirlo a la IA

Pide a tu asistente de IA autorizado:

Escribe un único SELECT de SQLite con customers(id, name) y orders(id, customer_id, status). Devuelve el ID de cada cliente y su número de pedidos pagados, incluido cero. Un pedido está pagado solo cuando status es igual a ‘paid’. Ordena por ID de cliente. Devuelve únicamente SQL.

Proporciona el esquema y ejemplos sintéticos, no registros privados de clientes. Para repasar las cláusulas, consulta nuestra guía para leer consultas SQL.

Define las filas esperadas antes de revisar el SQL generado. No dejes que el modelo decida sus propias respuestas de referencia.

2. Elige datos que revelen errores plausibles

Nuestro juego de prueba es un conjunto de datos pequeño y reutilizable:

ID de cliente Pedidos en los datos de prueba Número esperado de pedidos pagados
1 Dos pagados y uno cancelado 2
2 Ninguno 0
3 Uno cancelado 0

El segundo cliente detecta la pérdida de filas sin coincidencias; el tercero detecta filtros que eliminan clientes sin pedidos válidos. El primero detecta consultas que cuentan todos los pedidos en lugar de los pagados.

Un LEFT JOIN conserva a los clientes sin coincidencias. Mantén la condición de estado en ON: un filtro de la tabla derecha en WHERE puede eliminar las filas completadas con nulos, como explica la documentación de SELECT de SQLite. Además, COUNT(o.id) ignora nulos, mientras que COUNT(*) cuenta filas, incluida la fila de relleno de un cliente sin coincidencias. Consulta las definiciones de agregados de SQLite.

3. Ejecuta el comprobador y sus controles negativos

Guarda este código como check_ai_sql.py. Usa Python 3.10 o posterior con el módulo sqlite3 de la biblioteca estándar. La documentación de conexiones de Python describe las bases de datos :memory:.

import sqlite3
import sys
from pathlib import Path

REFERENCE = """
SELECT c.id, COUNT(o.id) AS paid_orders
FROM customers AS c
LEFT JOIN orders AS o
    ON o.customer_id = c.id AND o.status = 'paid'
GROUP BY c.id
ORDER BY c.id;
"""
EXPECTED = [(1, 2), (2, 0), (3, 0)]

if len(sys.argv) > 2:
    raise SystemExit("usage: python3 check_ai_sql.py [candidate.sql]")

db = sqlite3.connect(":memory:")
try:
    db.executescript("""
    CREATE TABLE customers(id INTEGER PRIMARY KEY, name TEXT NOT NULL);
    CREATE TABLE orders(
        id INTEGER PRIMARY KEY,
        customer_id INTEGER NOT NULL,
        status TEXT NOT NULL
    );
    INSERT INTO customers VALUES (1, 'Ada'), (2, 'Luis'), (3, 'Mina');
    INSERT INTO orders VALUES
        (101, 1, 'paid'), (102, 1, 'paid'),
        (103, 1, 'cancelled'), (104, 3, 'cancelled');
    """)
    controls = {
        "inner_join": REFERENCE.replace("LEFT JOIN", "JOIN"),
        "count_star": REFERENCE.replace("COUNT(o.id)", "COUNT(*)"),
        "where_filter": REFERENCE.replace(
            "ON o.customer_id = c.id AND o.status = 'paid'",
            "ON o.customer_id = c.id\nWHERE o.status = 'paid'",
        ),
    }
    for name, query in controls.items():
        rows = db.execute(query).fetchall()
        passed = rows == EXPECTED
        print(f"{name}: {'PASS' if passed else 'FAIL'} {rows}")
        if passed:
            raise RuntimeError(f"fixture failed to catch {name}")

    query = REFERENCE
    if len(sys.argv) == 2:
        query = Path(sys.argv[1]).read_text(encoding="utf-8")
    rows = db.execute(query).fetchall()
    passed = rows == EXPECTED
    print(f"candidate: {'PASS' if passed else 'FAIL'} {rows}")
    raise SystemExit(0 if passed else 1)
finally:
    db.close()

Ejecuta primero la consulta de referencia integrada:

python3 check_ai_sql.py

Salida esperada:

inner_join: FAIL [(1, 2)]
count_star: FAIL [(1, 2), (2, 1), (3, 1)]
where_filter: FAIL [(1, 2)]
candidate: PASS [(1, 2), (2, 0), (3, 0)]

Las tres líneas FAIL son controles negativos deliberados: deben rechazarse las consultas con pequeños errores. La referencia supera la prueba con código de salida 0; las consultas rechazadas siguen siendo inaceptables.

4. Comprueba una respuesta de IA revisada

Lee primero el SQL generado. Guarda únicamente su SELECT revisado en UTF-8 como candidate.sql, sin delimitadores Markdown. Después ejecuta:

python3 check_ai_sql.py candidate.sql

Los controles negativos siguen ejecutándose. La consulta candidata debe devolver exactamente [(1, 2), (2, 0), (3, 0)] en ese orden. Una discrepancia imprime candidate: FAIL y termina con código 1; los errores SQL también interrumpen la ejecución con un código distinto de cero. Copiar el SELECT de referencia del script en este archivo reproduce el resultado correcto.

Esto comprueba filas, cantidades y orden, no alias de columnas, velocidad ni corrección universal. Una base de datos en memoria no es un entorno de seguridad aislado. No ejecutes SQL arbitrario sin revisar, adjuntes archivos reales, cargues extensiones ni adaptes este ejemplo para conectarlo a una base de datos en producción.

5. Resuelve problemas y amplía las pruebas

  • Faltan clientes con cantidad cero: revisa el tipo de unión y la ubicación del filtro de estado.
  • Los ceros se convierten en unos: comprueba si COUNT(*) está contando filas sin coincidencias.
  • Valores correctos, orden incorrecto: exige el ORDER BY indicado; no ocultes el fallo ordenando solo el resultado en Python.
  • Falta el módulo sqlite3: usa una distribución de Python compilada con soporte para SQLite; este ejemplo no tiene una dependencia separada de pip.
  • El dialecto de producción es distinto: repite las pruebas en el motor real sobre tablas sintéticas desechables antes de aprobar una consulta.

Añade casos basados en fallos reales, como nombres visibles duplicados o tablas adicionales que multipliquen las filas al unirlas. Reserva otro juego de datos no visto para la revisión final después de cambiar la petición. Para resúmenes más complejos, consulta nuestra guía de agregación condicional.

Conclusión práctica

Pide la consulta a la IA, pero decide tú el resultado esperado. Los datos pequeños y los controles con errores deliberados revelan fallos que una comprobación de sintaxis no detecta. Superar este ejemplo justifica seguir probando, no ejecutar la consulta en producción.

Artículos relacionados

Últimos artículos