Verificar uma consulta SQL escrita por IA com resultados do SQLite calculados à mão
Trate uma consulta SQL gerada por IA como um rascunho e teste-a em um pequeno banco SQLite com respostas conhecidas. Compare o resultado da IA com uma tabela esperada calculada manualmente antes de usar a consulta em dados reais.
Conteúdo verificado 2026.09.20Inclui código de exemplo
Ver o sumário
A tradução foi feita com IA. Confira o código, as unidades e os valores junto com o original. A revisão por falantes nativos de cada idioma ainda não foi concluída. English
Para quem éEste guia é para quem usa IA para criar rascunhos de SQL e quer um teste pequeno e reproduzível antes de executar a consulta em dados importantes.
Preparação
Python 3.12 e um comando de terminal que inicie essa versão.
Uma pasta de trabalho em que os scripts possam criar pastas dentro de outputs.
Familiaridade básica com SELECT, JOIN, GROUP BY e funções de agregação.
É necessária apenas a biblioteca padrão do Python: csv, pathlib e sqlite3.
01Defina a pergunta de negócio antes de revisar o SQL
Suponha que o requisito seja: para cada cliente, informar o valor total dos pedidos PAID feitos durante agosto de 2026. Clientes sem pedidos que atendam às condições ainda devem aparecer com total 0.
Um assistente de IA propõe um INNER JOIN entre customers e orders, seguido de condições WHERE para status e date. A consulta parece razoável, mas o INNER JOIN remove clientes que não têm linhas de pedido correspondentes. Isso viola o requisito de incluir clientes com total zero.
02Crie um banco de dados SQLite sintético
O banco de dados a seguir é sintético e foi criado especificamente para este artigo. Ele tem três clientes e seis pedidos escolhidos para testar status pagos e não pagos, limites entre meses e um cliente sem pedido PAID em agosto.
Ana tem dois pedidos PAID em agosto: 40 e 60, então o total dela é 100. O pedido CANCELLED não conta. Ben tem um pedido válido em agosto no valor de 25; o pedido de setembro não conta. Cara tem apenas um pedido CANCELLED em agosto, então ainda precisa aparecer com 0.
customer_id
customer_name
Total PAID esperado de agosto
1
Ana
100
2
Ben
25
3
Cara
0
O total geral esperado é 125. Deve haver exatamente 3 linhas de saída porque o requisito diz que todos os clientes devem aparecer.
04Compare a consulta da IA com uma consulta revisada
A consulta da IA abaixo filtra os pedidos válidos na cláusula WHERE depois de um INNER JOIN. Cara não tem nenhum pedido PAID em agosto que corresponda, então desaparece completamente.
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;
A consulta revisada parte de todos os clientes e usa LEFT JOIN. Os filtros de pedidos são colocados dentro da condição ON para que clientes sem correspondência sejam mantidos. COALESCE converte o agregado NULL resultante em 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;
05Execute as duas consultas e compare com as linhas esperadas
Salve o script a seguir como ai_sql_check.py. Ele executa as duas consultas, compara os resultados com a tabela esperada calculada à mão e grava um relatório CSV de revisão. O banco sintético não é modificado.
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
06Confira a saída esperada
A consulta da IA deve retornar apenas Ana e Ben, produzindo 2 linhas e não correspondendo ao resultado esperado. A consulta revisada deve retornar todos os 3 clientes e corresponder exatamente à tabela calculada à mão.
Consulta
Linhas retornadas
Total geral esperado
Corresponde à tabela esperada
IA
2
125
Não
Revisada
3
125
Sim
O resultado da IA ainda pode ter o total geral correto de 125 mesmo estando errado, porque a linha obrigatória de Cara com valor zero está ausente. Por isso, verificar apenas os totais é insuficiente.
Inclua clientes com zero registros correspondentes nos dados sintéticos quando o requisito disser que eles devem aparecer.
Teste limites de data como August 31 e September 1.
Inclua status excluídos, como CANCELLED.
Execute o verificador novamente sem alterar OUTPUT_DIR. Ele deve parar com FileExistsError em vez de sobrescrever o relatório.
Problema comum
O que verificar
Linhas ausentes inesperadamente
Confira o tipo de JOIN e se filtros em WHERE removem linhas não correspondentes de um LEFT JOIN.
Totais altos demais
Procure joins um-para-muitos que dupliquem linhas antes da agregação.
Intervalo de datas incorreto
Use um limite inferior explícito e um limite superior exclusivo adequado ao formato de data armazenado.
NULL em vez de zero
Decida se correspondências ausentes devem permanecer NULL ou ser convertidas com COALESCE.
Total correto, mas detalhes errados
Compare todas as linhas esperadas, não apenas o total geral.
Este teste comprova apenas que a consulta revisada corresponde a este pequeno exemplo sintético. Esquemas reais podem conter relações duplicadas, valores NULL, timestamps, time zones, refunds ou regras de negócio adicionais. Amplie os dados de teste sempre que essas condições forem importantes.
Registro de execução e verificação
ChatGPT Python tool · Python 3.13.5 · standard library csv/pathlib/sqlite3
As linhas esperadas calculadas manualmente foram confirmadas como (1, Ana, 100), (2, Ben, 25) e (3, Cara, 0), com total geral de 125.
A consulta de IA retornou exatamente 2 linhas: Ana=100 e Ben=25. Cara foi omitida, portanto o resultado não correspondeu a EXPECTED.
A consulta revisada com LEFT JOIN retornou exatamente 3 linhas: Ana=100, Ben=25 e Cara=0, correspondendo exatamente a EXPECTED.
A execução principal imprimiu exatamente: 'Expected rows: 3.', 'AI rows: 2; matches expected: False.', 'Revised rows: 3; matches expected: True.' e 'Report: outputs/ai_sql_check_result/sql_check.csv'.
sql_check.csv continha um cabeçalho e duas linhas de dados. A linha AI registrou "[(1, 'Ana', 100), (2, 'Ben', 25)]" com False, e a linha REVISED registrou "[(1, 'Ana', 100), (2, 'Ben', 25), (3, 'Cara', 0)]" com True.
O resultado da IA ainda somava 125 apesar de omitir Cara, confirmando a afirmação do artigo de que apenas o total geral não é suficiente.
O hash SHA-256 de orders.sqlite permaneceu inalterado após executar o verificador, confirmando que o banco de dados foi aberto como somente leitura e não foi modificado.
Uma segunda execução do verificador foi interrompida com FileExistsError porque outputs/ai_sql_check_result já existia.
Limites da verificação
Não foram realizados testes de estresse separados para valores NULL em amount, relações de junção duplicadas, casos adicionais de limite de data como 31 de agosto, timestamps, fusos horários, reembolsos, bancos de dados malformados, falhas de permissão nem conjuntos de dados grandes.
O ambiente Python emitiu no stderr um aviso não relacionado de aquecimento da ferramenta de planilhas artifact-tool, mas ambos os scripts do artigo foram executados com sucesso e o verificador terminou com código 0 na execução de verificação.
Fontes de referência
As explicações e os exemplos são de elaboração própria. Os comportamentos e conceitos relacionados podem ser consultados nas fontes oficiais abaixo.
Transforme “automatize isso” em uma descrição de tarefa executável. Anexe uma amostra sintética sem dados sensíveis e um resultado esperado verificado manualmente para completar uma solicitação de script que totaliza registros de trabalho por equipe.
Verifique uma função de agregação com quatro linhas que você pode calcular manualmente e 12 unit tests. Verifique não apenas valores normais, mas também entrada vazia, zero, decimais e entrada inválida.
Compare um relatório de IA com os dados de origem, recalcule totais e percentuais e use um prompt para identificar afirmações sem respaldo e causas exageradas antes da entrega.
Trate uma regex gerada por IA como um rascunho, não como uma regra final. Monte uma pequena tabela de testes sintéticos, compare os resultados esperados com as correspondências reais, revise o padrão e salve um relatório de revisão antes de usá-lo em dados reais.