MySQL, MariaDB and PostgreSQL

N+1 query detection

An N+1 shows up in the database as a query whose call count grows with the rows another query returns. dbtempo compares call and row counts between two scans, pairs each such query with the table its filter points to, and flags the pair when the child query runs many times for each call of the parent.

How the rule decides

  • The child query filters with = on a column that references the parent table.
  • The parent query returns more than one row per call; a query that returns one row drives one child call, which is not a loop.
  • Between two scans the child ran often, and several times per parent call, in line with the rows the parent returned.
  • It reads statistics only, so it works on a database with no repository connected.

A worked example

A page lists fifty orders, then loads each order’s customer: one SELECT on orders, then SELECT * FROM customers WHERE id = ? fifty times. Over a day the customers query runs fifty times for every orders call. The fix is one query instead of fifty: a JOIN, or a single SELECT with WHERE id IN (…) for the fifty ids, which most ORMs call eager loading.

Finding it in your code

With a repository connected, dbtempo matches the child query to the SQL in your code, so the recommendation names the file that sends it. The loop itself usually sits where a relation is loaded lazily inside a loop; the fix is to load it up front.

What it costs

N+1 detection is a deterministic comparison of statistics and uses none of your monthly AI allowance.