A correlated subquery refers to an outer column, so its answer changes per row. Which products cost more than the average of their own category?
SELECT p.sku, p.category_id, p.price FROM products AS p
WHERE p.price > (SELECT AVG(q.price) FROM products AS q
WHERE q.category_id = p.category_id);+-----------+-------------+-------+ | sku | category_id | price | +-----------+-------------+-------+ | BK-SQL-01 | 3 | 44.50 | | BK-LAR-01 | 2 | 49.00 | | AC-MUG-01 | 5 | 12.50 | +-----------+-------------+-------+ 3 rows in set (0.001 sec)
The mug beats its category's 8.745. Alias both tables: a bare category_id inside would mean q's. Logically the subquery reruns per outer row; EXPLAIN FORMAT=TREE marks it "dependent", probing the index on q.category_id. Most EXISTS tests are correlated too.
Without an index on the correlation column, each outer row rescans the inner table. Index it, or build the inner result once: join a grouped derived table (Derived Tables and LATERAL), or use AVG(price) OVER (PARTITION BY category_id) (OVER and PARTITION BY). The subquery_to_derived optimizer switch, off by default, makes the first rewrite automatically.