How to Optimize Slow SQL Queries
SkillVeris Team
Data Science Team

The fastest way to optimize a slow SQL query is to run EXPLAIN, find the full-table scans, and add an index on the filtered or joined columns.
In this guide, you'll learn:
- Indexes are the single biggest lever, but only when they match the columns used in WHERE, JOIN, and ORDER BY clauses.
- Selecting only the columns you need and filtering early reduces the work the database must do.
- Functions applied to a column in WHERE prevent index use, so keep the column bare on one side of the comparison.
- The execution plan reveals scan types, join methods, and row estimates that pinpoint the real bottleneck.
1How Do You Optimize a Slow SQL Query?
The most reliable way to optimize a slow SQL query is to read its execution plan with EXPLAIN, identify where the database scans entire tables instead of using an index, and add an index on the columns used in WHERE, JOIN, and ORDER BY. Most dramatic speedups come from turning a full-table scan into an index lookup.
Optimization is a measurement-driven process, not guesswork. You find the bottleneck with the plan, make one targeted change, and measure again. This section walks through the highest-impact techniques in the order you should try them.
2Read the Execution Plan First
Before changing anything, ask the database how it runs your query. Every major database exposes this through EXPLAIN, and adding ANALYZE actually runs the query and reports real timings. The plan shows whether the database scans a whole table, uses an index, and how many rows it estimates at each step.
Look for sequential or full-table scans on large tables, big gaps between estimated and actual row counts, and expensive sort or nested-loop operations. These are your targets. The plan turns optimization from guesswork into a directed hunt.
- EXPLAIN SELECT ... # shows the plan without running it
- EXPLAIN ANALYZE SELECT ... # runs it and shows real timings (PostgreSQL)
- Look for: Seq Scan / Full Table Scan on large tables.
- Look for: rows estimate far from actual — stale statistics.
3Add the Right Indexes
An index is a sorted lookup structure that lets the database find rows without scanning the whole table, much like a book's index lets you find a topic without reading every page. Adding an index on the columns in your WHERE, JOIN, and ORDER BY clauses is usually the single biggest performance win.
For queries filtering on multiple columns, a composite index covering them in the right order helps most. The order matters: put the most selective or equality-filtered column first. But indexes are not free — they slow down writes and use storage, so index deliberately, not on every column.
💡Covering Indexes
If an index contains every column a query needs, the database answers entirely from the index without touching the table — a covering index. This can turn an already-fast query into an instant one.
4Write Index-Friendly Queries
Even with the right index, a query can accidentally prevent the database from using it. Wrapping an indexed column in a function, such as WHERE YEAR(created_at) = 2026, forces a scan because the index stores raw values, not function results. Rewrite it as a range on the bare column instead.
Similarly, leading wildcards in LIKE patterns, implicit type conversions, and OR conditions across different columns can all defeat indexes. Keeping the indexed column alone on one side of the comparison and using ranges keeps the index in play.
- Avoid: WHERE YEAR(created_at) = 2026 # function blocks the index
- Prefer: WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01'
- Avoid: WHERE name LIKE '%smith' # leading wildcard cannot use index
- Avoid comparing a column to a different type, forcing implicit conversion.
5Reduce the Work the Database Does
Beyond indexes, the fastest query is the one that touches the least data. Select only the columns you actually need rather than SELECT *, which avoids reading and transferring unnecessary data and enables covering indexes. Filter as early as possible so fewer rows flow into joins and aggregations.
Limit result sets with pagination, avoid needlessly recomputing the same subquery, and consider whether an expensive report can be precomputed into a summary table or materialized view. These reduce raw work independent of indexing.
Keep Statistics Fresh
The query planner relies on table statistics to choose a plan. If those statistics are stale after a big data change, it may pick a poor plan. Running ANALYZE (PostgreSQL) or updating statistics refreshes them so the planner makes good choices.
6Common Mistakes to Avoid
Optimization efforts often go sideways because of a few recurring missteps.
- Adding indexes blindly to every column, slowing writes and wasting storage.
- Optimizing without measuring, so you fix something that was never the bottleneck.
- Using SELECT * and transferring far more data than the application needs.
- Wrapping indexed columns in functions, silently disabling the index.
- Ignoring the N+1 pattern, where an application fires one query per row instead of a single join.
⚠️Measure, Don't Guess
The slowest part of a query is frequently not where you expect. Always capture timings before and after a change, or you may spend hours optimizing something that had no impact.
7Tools That Help You Tune
You do not have to optimize blind — every database ships with tools to surface slow queries and expose what they are doing. Slow query logs record statements that exceed a time threshold, giving you a ranked list of what to fix first. Combining that list with the execution plan turns tuning into a short, focused loop.
Beyond the built-in plan output, most engines expose statistics views and profiling tools that show buffer usage, temporary files, and time spent per step. Use them to confirm your change actually helped rather than trusting intuition.
- PostgreSQL: pg_stat_statements aggregates the most expensive queries.
- MySQL: the slow query log plus EXPLAIN ANALYZE.
- SQL Server: Query Store and execution plan viewer.
- Enable slow query logging in production to catch regressions early.
8Key Takeaways
Effective SQL tuning is systematic, not magical.
- Start with EXPLAIN to find full-table scans and expensive steps.
- Index the columns used in WHERE, JOIN, and ORDER BY — the biggest lever.
- Keep indexed columns bare in conditions so the index can be used.
- Select only needed columns and filter early to reduce work.
- Measure before and after every change; keep table statistics fresh.
9Frequently Asked Questions
Q: What is the first thing to do when a query is slow? A: Run EXPLAIN or EXPLAIN ANALYZE to see the execution plan. It reveals whether the database is scanning entire tables, which joins are expensive, and where the time goes, so you can target the real bottleneck instead of guessing.
Q: Do indexes always make queries faster? A: Indexes speed up reads that filter, join, or sort on the indexed columns, but they slow down inserts, updates, and deletes because the index must be maintained. They also use storage, so add them where they help specific queries rather than everywhere.
Q: Why is my index not being used? A: A common cause is wrapping the indexed column in a function or applying a leading-wildcard LIKE, both of which prevent the database from using the sorted index. Implicit type conversions and stale statistics can also lead the planner to skip the index.
Q: Is SELECT * bad for performance? A: It can be, because it reads and transfers columns your application does not need and prevents covering indexes from satisfying the query on their own. Selecting only the required columns reduces I/O and network traffic and often enables faster plans.
Related Reading
Get The Print Version
Download a PDF of this article for offline reading.
About the Publisher
SkillVeris Team
Data Science Team
Our data team shares real-world analytics, ML, and SQL insights grounded in industry practice.
View all postsRelated Posts
Never miss an update
Get the latest tutorials and guides delivered to your inbox.
No spam. Unsubscribe anytime.