← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

MySQL Subqueries

Database Intermediate
debt(d9/e3/b3/t7)
d9 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'silent in production until users hit it' (d9). The detection_hints field explicitly states automated detection is 'no', with only a code_pattern hint. No static analysis tool is listed. Correlated subqueries and IN vs EXISTS misuse execute silently and only reveal performance problems under production-level data volumes — invisible until users experience slow queries.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix states 'Replace correlated subqueries with a JOIN — use EXISTS instead of IN for large subquery result sets.' This is a targeted rewrite of the affected query or small set of queries — more than a one-liner because query logic must be restructured, but contained within one component or query file.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). The applies_to covers web and cli contexts broadly, but the debt is query-local — a bad subquery pattern imposes a performance tax only on the queries and code paths that call it. The rest of the codebase is generally unaffected unless the pattern is pervasive.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception is explicit: developers believe IN (SELECT ...) and EXISTS (SELECT ...) are always equivalent, but EXISTS short-circuits while IN evaluates everything. This contradicts the intuition that two logically equivalent constructs perform equivalently, and the correlated subquery N+1 behaviour also surprises developers familiar with ORM-level N+1 but not SQL-level equivalents.

About DEBT scoring →

Also Known As

SQL subquery correlated subquery MySQL derived table EXISTS vs IN MySQL

TL;DR

A query nested inside another — useful for filtering and deriving values, but correlated subqueries re-execute per outer row and can be O(n²).

Explanation

Subqueries can appear in SELECT (scalar), FROM (derived table), WHERE, or HAVING. Correlated subqueries reference the outer query and re-execute for each outer row — potentially catastrophically slow on large tables. Non-correlated subqueries execute once. IN (subquery) evaluates the full subquery; EXISTS short-circuits on first match — EXISTS is usually faster for large sets. MySQL 8+ improves subquery decorrelation automatically in some cases.

Common Misconception

IN (SELECT ...) and EXISTS (SELECT ...) are always equivalent. EXISTS short-circuits; IN evaluates everything. On large subqueries EXISTS is significantly faster.

Why It Matters

A correlated subquery in a WHERE or SELECT clause is the SQL equivalent of the N+1 problem — executing thousands of times on large datasets and turning a fast query into a slow one.

Common Mistakes

  • Using a correlated subquery in SELECT to fetch a related value — re-executes for every row.
  • Using IN with a large subquery instead of a JOIN or EXISTS.
  • Nesting subqueries more than two levels deep — readability and optimisability both suffer.

Avoid When

  • Avoid correlated subqueries in SELECT — replace with a JOIN.
  • Avoid deep nesting — flatten with CTEs (WITH clause) in MySQL 8 for readability.

When To Use

  • Use EXISTS for semi-join checks — checking whether any related row exists.
  • Use derived tables in FROM for complex aggregations that need to be joined back.

Code Examples

✗ Vulnerable
-- Correlated: re-runs for every user row → O(n²)
SELECT email,
       (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) AS cnt
FROM users u;
✓ Fixed
-- EXISTS short-circuits on first match
SELECT * FROM users u
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id AND o.status = 'paid');

-- Derived table in FROM (executes once)
SELECT u.email, stats.total
FROM users u
JOIN (SELECT user_id, SUM(total) AS total FROM orders GROUP BY user_id) stats
    ON stats.user_id = u.id;

Added 31 Mar 2026
Views 40
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 1 ping T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W 2 pings T 1 ping F 0 pings S 2 pings S 1 ping M 1 ping T 0 pings W 0 pings T 0 pings F 2 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 0 pings W
No pings yet today
No pings yesterday
Google 6 Scrapy 5 Perplexity 4 Unknown AI 4 Ahrefs 3 Claude 2 ChatGPT 1 Meta AI 1 Bing 1 Majestic 1 PetalBot 1
crawler 24 crawler_json 4 pre-tracking 1
DEV INTEL Tools & Severity
⚙ Fix effort: Medium
⚡ Quick Fix
Replace correlated subqueries with a JOIN — use EXISTS instead of IN for large subquery result sets
📦 Applies To
web cli
🔗 Prerequisites
🔍 Detection Hints
SELECT (SELECT ... WHERE outer.id = ...) or WHERE col IN (SELECT ...)
Auto-detectable: ✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: Medium False Positives: Medium ✗ Manual fix Fix: Medium Context: Line Tests: Update


✓ schema.org compliant