Advanced query optimization techniques

Maria Garcia Feb 2026
2 tabs
-- EXPLAIN ANALYZE (actual execution statistics)
EXPLAIN ANALYZE
SELECT u.username, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at >= '2024-01-01'
GROUP BY u.id, u.username
ORDER BY order_count DESC
LIMIT 10;

-- Key metrics to watch:
-- Planning Time: Time spent planning query
-- Execution Time: Time spent executing
-- Rows: Estimated vs actual rows
-- Cost: Planner's cost estimate

-- Covering index (includes all needed columns)
CREATE INDEX idx_orders_user_id_created_at
  ON orders (user_id, created_at)
  INCLUDE (total, status);

-- Query uses index-only scan (no table access)
SELECT user_id, created_at, total, status
FROM orders
WHERE user_id = 123
ORDER BY created_at DESC;

-- Partial index (smaller, faster)
CREATE INDEX idx_orders_active
  ON orders (user_id, created_at)
  WHERE status = 'active';

-- Only active orders in index
SELECT * FROM orders
WHERE user_id = 123
  AND status = 'active'
ORDER BY created_at DESC;

-- Expression index
CREATE INDEX idx_users_lower_email
  ON users (LOWER(email));

-- Case-insensitive search uses index
SELECT * FROM users
WHERE LOWER(email) = 'user@example.com';

-- Avoid SELECT * (fetch only needed columns)
-- Bad:
SELECT * FROM users WHERE id = 123;

-- Good:
SELECT id, username, email FROM users WHERE id = 123;

-- Limit early to reduce processing
-- Bad:
SELECT * FROM (
  SELECT * FROM large_table ORDER BY created_at DESC
) sub
LIMIT 10;

-- Good:
SELECT * FROM large_table
ORDER BY created_at DESC
LIMIT 10;

-- Join optimization: smaller table first
-- Bad join order:
SELECT *
FROM huge_table h
JOIN small_table s ON h.id = s.huge_id;

-- Better: Let planner decide, but ensure statistics are current
ANALYZE huge_table;
ANALYZE small_table;

-- Subquery to CTE transformation
-- Subquery (may execute multiple times):
SELECT
  u.username,
  (SELECT COUNT(*) FROM orders WHERE user_id = u.id) AS order_count
FROM users u;

-- CTE (executes once):
WITH order_counts AS (
  SELECT user_id, COUNT(*) AS count
  FROM orders
  GROUP BY user_id
)
SELECT u.username, COALESCE(oc.count, 0) AS order_count
FROM users u
LEFT JOIN order_counts oc ON u.id = oc.user_id;

-- EXISTS vs IN
-- EXISTS (often faster, stops at first match):
SELECT username
FROM users u
WHERE EXISTS (
  SELECT 1 FROM orders o
  WHERE o.user_id = u.id AND o.status = 'completed'
);

-- IN (may be slower for large result sets):
SELECT username
FROM users
WHERE id IN (
  SELECT DISTINCT user_id FROM orders WHERE status = 'completed'
);

-- UNION ALL vs UNION
-- UNION ALL (no deduplication, faster):
SELECT id, name FROM products_active
UNION ALL
SELECT id, name FROM products_archived;

-- UNION (deduplicates, slower):
SELECT id, name FROM products_active
UNION
SELECT id, name FROM products_archived;

-- Batch updates (avoid row-by-row)
-- Bad:
-- UPDATE products SET price = price * 1.1 WHERE id = 1;
-- UPDATE products SET price = price * 1.1 WHERE id = 2;
-- ...

-- Good:
UPDATE products
SET price = price * 1.1
WHERE category_id = 5;

-- Filter before join
-- Bad:
SELECT o.*
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.created_at >= '2024-01-01';

-- Better:
SELECT o.*
FROM (
  SELECT * FROM orders
  WHERE created_at >= '2024-01-01'
) o
JOIN users u ON o.user_id = u.id;

-- Or use CTE for clarity:
WITH recent_orders AS (
  SELECT * FROM orders
  WHERE created_at >= '2024-01-01'
)
SELECT o.*
FROM recent_orders o
JOIN users u ON o.user_id = u.id;
2 files · sql Explain with highlit

Query optimization maximizes performance through efficient execution plans. I analyze queries with EXPLAIN ANALYZE. Understanding sequential scans vs index scans guides optimization. Join order affects performance dramatically. Subquery optimization via CTEs or lateral joins. Avoiding SELECT * reduces I/O. Limiting result sets early improves efficiency. Covering indexes eliminate table lookups. Partial indexes reduce index size. Expression indexes handle computed columns. Understanding query planner statistics prevents poor plans. Prepared statements cache execution plans. Partition pruning skips irrelevant partitions. Proper optimization transforms slow queries into fast ones. Query optimization is iterative—measure, optimize, verify improvement.