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

MySQL Subqueries

database Intermediate

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 14
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 1 ping T 0 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T
No pings yet today
No pings yesterday
Perplexity 4 Google 4 Unknown AI 3 ChatGPT 1 Ahrefs 1
crawler 10 crawler_json 2 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