On this page
How to Debug AI-Generated Database Queries That Return Wrong Results
Your AI wrote a database query. It runs, but returns the wrong data — missing rows, wrong counts, stale results. Here's how to extract the raw SQL, test it directly, and fix it without the AI rewriting your whole data layer.
Quick answer
- Don’t debug the ORM code — debug the generated SQL.
- Extract raw SQL from the ORM, run it directly against the database, and compare results.
- Common AI mistakes: wrong JOIN type, missing WHERE clause, N+1 queries, type mismatches in parameters.
- Full system: How to Debug AI-Generated Code: A Complete System
The problem: ORM code looks right, query returns wrong data
# AI-generated: looks correct, returns wrong data
def get_active_users_with_orders():
users = User.query.filter(User.is_active == True).all() # line 1
result = []
for user in users:
orders = Order.query.filter(Order.user_id == user.id).all() # N+1!
if orders: # line 4
result.append({"user": user.name, "count": len(orders)})
return result # returns 3 users when there should be 5The ORM code is clean. No syntax errors. The bug is invisible in Python — it’s in the SQL the ORM generates and in the N+1 pattern that hides performance problems.
Extract the raw SQL: the ORM is a black box — open it
Every ORM has a way to see the generated SQL. Use it.
# SQLAlchemy
from sqlalchemy import create_engine
engine = create_engine("postgresql://...", echo=True) # logs all SQL
# Or for a single query:
query = session.query(User).filter(User.is_active == True)
print(str(query)) # SELECT users.id, users.name, ... FROM users WHERE users.is_active = true
# Django
from django.db import connection
print(queryset.query) # the SQL
print(connection.queries) # all queries in this request
# Active Record (Rails)
puts User.where(active: true).to_sqlNow copy that SQL and run it directly in your database client:
-- Run the AI-generated SQL directly
SELECT users.id, users.name, users.email
FROM users
WHERE users.is_active = true;
-- Verify: does this return the right number of rows?
-- If yes: the SQL is correct, the bug is in Python processing
-- If no: the SQL is wrong, fix the query firstCommon AI-generated query bugs
1. Wrong JOIN type
# AI wrote INNER JOIN, should be LEFT JOIN
# INNER JOIN drops users with no orders
query = session.query(User).join(Order)
# SQL: SELECT ... FROM users INNER JOIN orders ON ...
# Fix: explicit left join
query = session.query(User).outerjoin(Order)Symptom: “Missing” rows — the query returns fewer results than expected because rows without a match are dropped.
2. N+1 query problem
# AI wrote: one query to get users, then one query per user to get orders
users = User.query.all() # 1 query
for user in users:
orders = Order.query.filter(...) # N queries (one per user)
# Total: 101 queries for 100 usersSymptom: Slow response, high database load. Not a wrong-result bug — a performance bug that looks correct.
Fix: eager loading.
# One query instead of N+1
users = User.query.options(joinedload(User.orders)).all()3. Type mismatch in filter parameters
# AI wrote: filter by string, column is integer
user_id = request.args.get("user_id") # "42" (string)
user = User.query.filter(User.id == user_id).first()
# SQL: SELECT ... WHERE users.id = '42'
# Postgres: type error or implicit cast that skips the indexSymptom: Slow query, or query returns no results when you know the data exists.
Fix: Explicit type conversion.
user_id = int(request.args.get("user_id"))4. Filter on the wrong column
# AI wrote: filter by user.id, should be order.user_id
orders = Order.query.filter(Order.id == user_id).all()
# Returns orders WHERE order.id = 42, not WHERE user_id = 42Symptom: Returns wrong data (orders belonging to wrong user) or no data.
Check: Read the WHERE clause in the generated SQL. Does it reference the column you intended?
5. ORM defaults hiding data
# AI didn't know about soft-delete column
users = User.query.all()
# ORM has a default filter: WHERE deleted_at IS NULL
# AI's code doesn't account for thisSymptom: “Missing” rows — the ORM is filtering them out and the AI didn’t know.
The debug checklist
- Extract SQL: log or print the generated SQL
- Run directly: execute it in a database client with sample parameters
- Check row count: does the raw SQL return the expected number of rows?
- Check JOIN type: INNER vs LEFT — which is correct for this query?
- Check WHERE clause: is it filtering on the right column with the right type?
- Check for N+1: count queries per request — if it grows with data size, you have N+1
- Check ORM defaults: does the model have default scopes or filters?
When to bypass the ORM entirely
If the AI’s ORM code is too complex to debug, write the raw SQL and test it:
# Fall back to raw SQL for debugging
result = db.execute(
"SELECT u.name, COUNT(o.id) as order_count "
"FROM users u "
"LEFT JOIN orders o ON o.user_id = u.id "
"WHERE u.is_active = true "
"GROUP BY u.id"
).fetchall()
# Now you know the correct result. Does the ORM produce the same?This tells you whether the bug is in the query logic or in the Python processing of results.
Where this bites vibecoders
The AI generates ORM code that looks clean. The vibecoder runs the endpoint, sees data, ships it. Three weeks later, users report missing orders — an INNER JOIN was silently dropping rows. Extracting the raw SQL and running it directly reveals the bug in 30 seconds.