2 min readUpdated 2026-10-10

SQL Query Debugging Checklist

A checklist for formatting SQL, finding dangerous updates, checking joins, reviewing filters, and preparing safer database debugging sessions.

Maintained by Ali Aygör. Report a correction.

Separate syntax, correctness and performance

A formatted SQL statement is easier to inspect, but readability does not prove correctness or efficiency. Start by identifying the database and dialect, the actual parameter values, the expected result and the context in which the statement ran.

Keep a redacted original alongside the formatted copy. ORM logs can contain placeholders, incomplete statements or extra escaping. Reconstructing a query with guessed parameter values can produce a completely different plan and misleading results.

Check the result's intended grain

Determine what one output row is supposed to represent. A join can multiply rows even when every predicate is syntactically valid. Compare counts before and after joins, and inspect keys that should be unique.

In the example below, a customer with several orders appears several times. If the expected result is one row per customer, choose an explicit aggregation or existence condition rather than adding DISTINCT without understanding the duplication.

SELECT c.id, o.id AS order_id
FROM customers c
JOIN orders o ON o.customer_id = c.id
WHERE o.status = 'pending';
-- Expected grain: one row per pending order, not one per customer.

Measure with the database's own plan

Use EXPLAIN to inspect the planner's assumptions and compare them with representative execution data where appropriate. Look at row estimates, scans, joins and resource use in context; an index scan is not automatically better than every sequential scan.

Be careful with EXPLAIN ANALYZE: it executes the statement. Do not treat it as a harmless preview for a production write. Follow your environment's transaction, permission and rollback practices before measuring statements that change data.

Review write scope and transaction behavior

Inspect each UPDATE, DELETE, DROP and TRUNCATE statement independently. A WHERE in a comment, another statement or an unrelated subquery does not establish a safe write scope. Check the affected rows with an appropriate read-only comparison before executing a change.

The browser analyzer uses simple text patterns and cannot know row counts, triggers, locks or permissions. A lack of warnings is therefore not evidence that the statement is safe. Confirm retries and transaction boundaries if duplicate work is part of the failure.

Create a small reproducible case

Record the schema shape, minimal representative data, parameters, expected result and observed result. Remove credentials and customer values. Include the database version and relevant execution evidence so another developer can reason about the same case.

After changing the query or index, rerun both the failing case and a control case with realistic cardinality. Compare result correctness first, then timings and plans. Faster output that omits valid rows is still a regression.

Specifications and references

Continue the investigation

Related tools

Browse all developer tools

Related workflows