🗄️SQL Templates

Subquery Patterns

Nested queries in WHERE, SELECT, and FROM clauses

Explanation

Use subqueries to perform complex queries by nesting SELECT statements.

Examples

Subquery in WHERE
Input
WHERE id IN (SELECT user_id FROM orders)
Output
Users who have placed orders
Subquery in SELECT
Input
SELECT (SELECT COUNT(*) FROM orders)
Output
Include count in result

Code Examples

SQL
-- Subquery in WHERE with IN
SELECT * 
FROM users 
WHERE id IN (
  SELECT user_id 
  FROM orders 
  WHERE status = 'completed'
);

-- NOT IN subquery
SELECT * 
FROM users 
WHERE id NOT IN (
  SELECT user_id 
  FROM orders
);

-- Subquery with comparison
SELECT * 
FROM products 
WHERE price > (
  SELECT AVG(price) 
  FROM products
);

-- Correlated subquery
SELECT 
  u.name,
  (SELECT COUNT(*) 
   FROM orders o 
   WHERE o.user_id = u.id) AS order_count
FROM users u;

-- EXISTS subquery
SELECT * 
FROM users u
WHERE EXISTS (
  SELECT 1 
  FROM orders o 
  WHERE o.user_id = u.id
);

-- NOT EXISTS
SELECT * 
FROM users u
WHERE NOT EXISTS (
  SELECT 1 
  FROM orders o 
  WHERE o.user_id = u.id
);

-- Subquery in FROM (derived table)
SELECT 
  category,
  avg_price
FROM (
  SELECT 
    category,
    AVG(price) AS avg_price
  FROM products
  GROUP BY category
) AS product_stats
WHERE avg_price > 100;

-- Subquery with JOIN
SELECT 
  u.name,
  recent_orders.order_count
FROM users u
JOIN (
  SELECT 
    user_id,
    COUNT(*) AS order_count
  FROM orders
  WHERE created_at > NOW() - INTERVAL '30 days'
  GROUP BY user_id
) AS recent_orders ON u.id = recent_orders.user_id;

-- Multiple subqueries
SELECT 
  name,
  (SELECT COUNT(*) FROM orders WHERE user_id = users.id) AS orders,
  (SELECT SUM(total) FROM orders WHERE user_id = users.id) AS total_spent
FROM users;

-- Subquery with ANY
SELECT * 
FROM products 
WHERE price > ANY (
  SELECT price 
  FROM products 
  WHERE category = 'electronics'
);

-- Subquery with ALL
SELECT * 
FROM products 
WHERE price > ALL (
  SELECT price 
  FROM products 
  WHERE category = 'books'
);

💡 Tips

  • EXISTS is often faster than IN for large datasets
  • Correlated subqueries run for each row (can be slow)
  • Use JOINs instead of subqueries when possible
  • Subqueries in SELECT run for each row
  • Derived tables (FROM subquery) need aliases
  • Consider WITH (CTE) for complex subqueries
  • Test subquery performance vs JOIN

⚠️ Common Pitfalls

  • Correlated subqueries can be very slow
  • IN with large subquery results is slow
  • NULL handling in NOT IN can cause issues
  • Subqueries in SELECT multiply execution time
  • Missing alias on derived table causes error
  • Too many nested subqueries hurt readability