Skip to main content

Why AI-generated SQL is slow

Quick answer: AI writes SQL that's locally correct against the code it can see, but blind to the database it can't — the indexes, row counts, data distribution, and query plans. So it ships N+1 loops, filters on unindexed columns, SELECT *, unbounded results, and functions that disable indexes. The code reads clean; the slowness is a database fact the model never had access to.

The one reason behind all the others

Performance lives in the database, not the source. Your coding agent (Claude Code, Cursor, Copilot) can read your schema and your query, but it does not see:

  • which columns are actually indexed,
  • how many rows the table holds,
  • the distribution of your data (is status = 'pending' rare or half the table?),
  • or the query plan the planner will choose.

Every performance bug below is a special case of that blindness. The AI writes code that would be correct against a small, evenly distributed, fully indexed table — and then it meets production.

The five patterns

1. N+1 query loops

The agent loads a list, then accesses a related object in a loop, and the ORM fires one query per row. It reads perfectly — for post in posts: post.author — and it's the most common ORM performance bug. See how to find N+1 queries.

2. Filtering or joining on unindexed columns

WHERE customer_id = $1 is instant with an index and a full table scan without one. The agent has no idea which of your columns are indexed, so it writes the natural predicate and hopes. On a big table that's a Seq Scan — see reading an EXPLAIN plan.

3. SELECT * instead of the columns you need

SELECT * pulls every column, moves more data than the app uses, and defeats index-only scans. The agent defaults to it because it doesn't know which columns matter.

4. No LIMIT

Fetching an unbounded result set is fine on 100 rows and a memory problem on 10 million. "Get the users" becomes SELECT * FROM users with no ceiling.

5. Functions that disable the index (non-sargable predicates)

This one is subtle. A predicate like:

WHERE lower(email) = 'a@b.com' -- index on email is NOT used
WHERE created_at::date = '2026-01-01' -- index on created_at is NOT used

wraps an indexed column in a function, so the planner can't use the plain index and falls back to a sequential scan. The fix is to index the expression or rewrite the predicate:

CREATE INDEX ON users (lower(email)); -- match the expression
-- or compare the raw column with a range instead of a function:
WHERE created_at >= '2026-01-01' AND created_at < '2026-01-02';

Leading-wildcard LIKE '%foo' has the same problem — a plain B-tree index can't serve it.

Why does this keep happening?

Because none of it is visible in the code. Whether a column is indexed, how big the table is, whether the planner picks the index — these are runtime facts about your specific database. The model reproduces patterns from its training data (lots of SELECT *, lots of lazy-loading tutorials) and writes code that is idiomatic and locally right. The more SQL your team generates with AI, the more of these ship — not because the AI is bad, but because it's optimizing code it can see against a database it can't.

How do I catch it?

  • Review new queries against the actual schema and indexes, not just for correctness.
  • Rank real load with pg_stat_statements and pull plans for the top offenders.
  • Give whatever writes your SQL — human or agent — the database context it needs to reason about performance.

How DBGorilla helps

DBGorilla closes exactly this gap: it connects to your database read-only and gives your AI coding agent (Claude Code, Cursor) the real context — the indexes, row counts, pg_stat_statements load, and query plans — so the agent can catch an N+1, a missing index, or a non-sargable predicate the way a senior engineer would in review. It surfaces and explains through your agent; it does not touch production or rewrite anything on its own. Get started free →

FAQ

Why is AI-generated SQL often slow? Performance is a property of the database, not the code. The agent sees the query but not the indexes, row counts, distribution, or plan — so its locally-correct SQL only reveals its cost against real data.

What are the most common performance bugs in AI-written SQL? N+1 loops, filtering/joining on unindexed columns, SELECT *, no LIMIT, and wrapping an indexed column in a function that disables the index.

Why does wrapping a column in a function make a query slow? WHERE lower(email) = $1 isn't sargable against a plain index on email, so the planner does a sequential scan. Index the expression or compare the column directly.

Does using an AI coding agent mean my SQL will be slow? No — not if the agent has database context. Give it the indexes, row counts, plans, and stats and it can catch the same issues a reviewer would.