📡 You're offline — showing cached content
New version available!
Quick Access
PHP Intermediate Featured

SQL Query Optimization: 10 Techniques That Actually Work

10 SQL optimization techniques with before/after examples — indexes, covering indexes, NOT EXISTS vs NOT IN, LIMIT pushdown, and partition pruning.

EzyCoders Admin November 29, 2025 11 min read 0 views
SQL Query Optimization 10 Techniques Guide
Share: Twitter LinkedIn WhatsApp

SQL Query Optimization Techniques

Ten targeted techniques that deliver measurable performance improvements. These are the practical fixes applied in production systems — not theoretical advice.

-- 1. Index your WHERE, JOIN, ORDER BY columns
-- Before (ALL scan, 1M rows):
EXPLAIN SELECT * FROM orders WHERE status = 'pending';
-- After:
ALTER TABLE orders ADD INDEX idx_status (status);

-- 2. Composite indexes — order matters (leftmost prefix rule)
-- For queries: WHERE user_id = ? AND created_at > ?
ALTER TABLE orders ADD INDEX idx_user_date (user_id, created_at);

-- 3. Avoid functions on indexed columns (breaks index usage)
-- BAD:
SELECT * FROM users WHERE YEAR(created_at) = 2026;
-- GOOD:
SELECT * FROM users WHERE created_at BETWEEN '2026-01-01' AND '2026-12-31 23:59:59';

-- 4. Use LIMIT early in subqueries
-- BAD: sorts all, then limits
SELECT * FROM posts ORDER BY views DESC LIMIT 10;
-- GOOD: add index on views to avoid filesort
ALTER TABLE posts ADD INDEX idx_views (views);

-- 5. Avoid SELECT *
-- BAD: fetches all columns including large TEXT/BLOB
SELECT * FROM posts WHERE user_id = 1;
-- GOOD:
SELECT id, title, slug, published_at FROM posts WHERE user_id = 1;

-- 6. Use covering indexes for hot queries
ALTER TABLE posts ADD INDEX idx_cover (status, published_at, id, title);
SELECT id, title FROM posts WHERE status = 'published' ORDER BY published_at DESC;
-- Extra: Using index (no table access!)

-- 7. Rewrite NOT IN with NOT EXISTS (safer with NULLs)
-- BAD (fails with NULLs in subquery):
SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM banned);
-- GOOD:
SELECT u.* FROM users u
WHERE NOT EXISTS (SELECT 1 FROM banned b WHERE b.user_id = u.id);

-- 8. Partition large tables
ALTER TABLE logs PARTITION BY RANGE (UNIX_TIMESTAMP(created_at)) (
    PARTITION p2024 VALUES LESS THAN (UNIX_TIMESTAMP('2025-01-01')),
    PARTITION p2025 VALUES LESS THAN (UNIX_TIMESTAMP('2026-01-01')),
    PARTITION p2026 VALUES LESS THAN MAXVALUE
);
-- Queries with WHERE created_at scan only relevant partition!

-- 9. COUNT(*) vs COUNT(column)
-- COUNT(*) counts all rows (fastest, uses index)
-- COUNT(col) counts non-NULL values (slightly slower)
SELECT COUNT(*) FROM posts WHERE status = 'published';  -- fast

-- 10. Use EXPLAIN before deploying any new query
EXPLAIN SELECT p.*, u.name FROM posts p JOIN users u ON u.id = p.user_id
WHERE p.category_id = 3 ORDER BY p.views DESC LIMIT 20;
-- Check: type != ALL, key is not NULL, rows is reasonable

Q: When is an index harmful?

On write-heavy tables: every INSERT/UPDATE/DELETE must also update all indexes — too many indexes slow writes more than they help reads. On low-cardinality columns: an index on a boolean or status column with 2-3 values often makes MySQL choose a full scan anyway. Rule: only add indexes for actual slow queries measured with EXPLAIN.

EzyCoders Admin
Written by
EzyCoders Admin

Team Lead and Full-Stack Developer with experience in PHP, JavaScript, SQL, DSA, and System Design. Passionate about software engineering, scalable web technologies, and helping developers prepare for coding interviews and tech careers through practical tutorials and professional guidance.

Comments (0)

No comments yet. Be the first!

Leave a Comment