Table of Contents

SQL Performance Optimization Guide

A practical guide to optimizing SQL queries and database performance.

Core Principles

When optimizing SQL, focus on these goals:


1. Use EXPLAIN to Analyze Queries

Before optimizing a query, inspect its execution plan using `EXPLAIN`.

Why use EXPLAIN?

`EXPLAIN` shows:

Example

EXPLAIN
SELECT *
FROM orders
WHERE customer_id = 10
ORDER BY created_at DESC;

Things to watch for

Problem Meaning
Full table scan Database scans entire table
Using temporary Temporary table created internally
Using filesort Expensive sorting operation
Large row count Too many rows scanned
No index used Missing or incorrect index

Goal

Queries should:


2. Add Proper Indexes

Indexes are one of the biggest performance improvements in SQL.

Add indexes on commonly used columns

Create indexes for columns frequently used in:

Example

-- WHERE condition
CREATE INDEX idx_orders_customer_id
ON orders(customer_id);
 
-- ORDER BY
CREATE INDEX idx_orders_created_at
ON orders(created_at);
 
-- JOIN
CREATE INDEX idx_orders_user_id
ON orders(user_id);

Composite Index Example

If queries often filter by multiple columns:

SELECT *
FROM orders
WHERE customer_id = 10
AND STATUS = 'paid';

Use a composite index:

CREATE INDEX idx_orders_customer_status
ON orders(customer_id, STATUS);

Important Notes


3. Select Only Needed Columns

Avoid using:

SELECT *
FROM users;

Why is `SELECT *` bad?

Better

SELECT id, name, email
FROM users;

Only select the columns your application actually needs.


4. Use LIMIT for Large Queries

Fetching huge datasets is expensive.

Bad

SELECT *
FROM logs
ORDER BY created_at DESC;

Better

SELECT id, message, created_at
FROM logs
ORDER BY created_at DESC
LIMIT 100;

Benefits


5. Optimize JOIN Operations

JOINs are expensive when tables are large.

Ensure JOIN columns are indexed

SELECT o.id, u.name
FROM orders o
JOIN users u ON o.user_id = u.id;

Recommended indexes:

CREATE INDEX idx_orders_user_id ON orders(user_id);

Usually, primary keys are already indexed automatically.

Avoid unnecessary JOINs

Do not JOIN tables if the data is not needed.

Select only required columns

SELECT o.id, u.name
FROM orders o
JOIN users u ON o.user_id = u.id;

Avoid:

SELECT *
FROM orders o
JOIN users u ON o.user_id = u.id;

6. Avoid N+1 Query Problems

The N+1 problem happens when an application executes one query to fetch a list, then runs additional queries for each item.

Bad Example

1 query to get users
+ 100 queries to get each user's orders
= 101 queries total

Example in application code

$users = User::all();
 
foreach ($users as $user) {
    echo $user->orders;
}

This may generate hundreds of SQL queries.


Solution 1 — Eager Loading

Load related data in a single query.

$users = User::with('orders')->get();

Benefits:


Solution 2 — Use JOIN

Sometimes a JOIN is more efficient.

SELECT users.name, orders.total
FROM users
JOIN orders ON users.id = orders.user_id;

Use JOIN when:


7. Use Query Caching

Frequently executed queries should be cached.

Good candidates for caching

Example Flow

Application
    ↓
Check cache
    ↓
If cache exists → return cached data
If not:
    Query database
    Store result in cache
    Return result

Benefits


8. Consider Denormalization Carefully

Normalization improves data consistency, but too many JOINs can hurt performance.

Sometimes it is beneficial to combine data from multiple tables into one table.

Example

Instead of:

orders
users
products
addresses

You may store some duplicated data directly in `orders`:

orders:
  customer_name
  customer_email
  shipping_address

This reduces expensive JOIN operations.


Benefits

Downsides

Rule

Only denormalize after identifying real performance bottlenecks.


9. Pagination Instead of Loading Everything

Avoid loading all rows at once.

Bad

SELECT *
FROM products;

Better

SELECT id, name, price
FROM products
LIMIT 20 OFFSET 0;

Even Better (Keyset Pagination)

SELECT id, name, price
FROM products
WHERE id > 100
ORDER BY id
LIMIT 20;

Keyset pagination is usually faster for large datasets.


10. Monitor Slow Queries

Enable slow query logging in production.

Monitor

Tools


Optimization Checklist

Before deploying a query, check:


Practical Recommendations

Situation Recommended Solution
Slow `WHERE` query Add index
Slow sorting Index `ORDER BY` columns
Too many ORM queries Use eager loading
Heavy repeated reads Add cache
Complex JOINs on huge tables Consider denormalization
Large result sets Use pagination
Slow query unknown cause Use `EXPLAIN`

Rule of Thumb

Do not optimize blindly — optimize based on actual bottlenecks.