Databases 14 min read

How to Supercharge MySQL Queries: 8 Proven Optimization Techniques

This article walks through eight common MySQL performance pitfalls—including inefficient LIMIT usage, implicit type conversion, sub‑query updates, mixed sorting, EXISTS clauses, condition push‑down, early range reduction, and intermediate result push‑down—and shows how to rewrite each query to achieve dramatic speed improvements, often reducing execution time from seconds to milliseconds.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
How to Supercharge MySQL Queries: 8 Proven Optimization Techniques

1. LIMIT Clause

Pagination is a frequent use case that can easily become a performance bottleneck. A naïve query such as

SELECT * FROM operation WHERE type = 'SQLStats' AND name = 'SlowLog' ORDER BY create_time LIMIT 1000, 10;

forces the engine to scan from the beginning even when an index exists, because the database does not know where the million‑th row starts. Rewriting the query to use the previous page's maximum timestamp eliminates the offset:

SELECT * FROM operation WHERE type = 'SQLStats' AND name = 'SlowLog' AND create_time > '2017-03-16 14:00:00' ORDER BY create_time LIMIT 10;

The execution time becomes constant regardless of table size.

2. Implicit Type Conversion

When a column type does not match the literal type, MySQL converts the column value, which can invalidate indexes. For example:

SELECT * FROM my_balance b WHERE b.bpn = 14000000123 AND b.isverified IS NULL;

MySQL converts bpn (VARCHAR) to a number, causing a full index scan. The fix is to ensure the literal matches the column type or cast explicitly.

3. Update/Delete with Joins

MySQL 5.6 introduced materialized sub‑queries for SELECT only; UPDATE/DELETE still use dependent sub‑queries, leading to poor performance. An example update:

UPDATE operation o SET status = 'applying' WHERE o.id IN (SELECT id FROM (SELECT o.id FROM operation o WHERE o.group = 123 AND o.status NOT IN ('done') ORDER BY o.parent, o.id LIMIT 1) t);

produces a DEPENDENT SUBQUERY plan. Rewriting it as a JOIN changes the plan to DERIVED and drops execution time from seconds to milliseconds:

UPDATE operation o JOIN (SELECT o.id FROM operation o WHERE o.group = 123 AND o.status NOT IN ('done') ORDER BY o.parent, o.id LIMIT 1) t ON o.id = t.id SET status = 'applying';

4. Mixed Sorting

MySQL cannot use an index for mixed ASC/DESC sorting. The original query:

SELECT * FROM my_order o INNER JOIN my_appraise a ON a.orderid = o.id ORDER BY a.is_reply ASC, a.appraise_time DESC LIMIT 0, 20;

results in a full table scan. Splitting the query by the binary is_reply flag and UNION‑ALLing the two parts allows the optimizer to use indexes, reducing runtime from 1.58 s to 2 ms.

5. EXISTS Clause

MySQL treats EXISTS as a nested sub‑query. The following query:

SELECT * FROM my_neighbor n LEFT JOIN my_neighbor_apply sra ON n.id = sra.neighbor_id AND sra.user_id = 'xxx' WHERE n.topic_status < 4 AND EXISTS (SELECT 1 FROM message_info m WHERE n.id = m.neighbor_id AND m.inuser = 'xxx') AND n.topic_type <> 5;

produces a DEPENDENT SUBQUERY plan. Rewriting it as a straight JOIN eliminates the sub‑query and drops execution time from 1.93 s to 1 ms.

6. Condition Push‑Down

External predicates cannot be pushed into certain sub‑queries, such as aggregation, LIMIT, UNION, or scalar sub‑queries. Example:

SELECT * FROM (SELECT target, COUNT(*) FROM operation GROUP BY target) t WHERE target = 'rm-xxxx';

After moving the predicate into the derived table, the plan becomes a simple index lookup, dramatically improving performance.

7. Early Range Reduction

When the final ORDER BY and WHERE clauses target the leftmost table, sorting can be performed before the joins. The original query scans ~900 k rows before sorting:

SELECT * FROM my_order o LEFT JOIN my_userinfo u ON o.uid = u.uid LEFT JOIN my_productinfo p ON o.pid = p.pid WHERE o.display = 0 AND o.ostaus = 1 ORDER BY o.selltime DESC LIMIT 0, 15;

By first selecting and sorting the my_order rows, then joining the smaller result set, execution time drops to about 1 ms.

8. Intermediate Result Push‑Down

When a sub‑query returns a large intermediate result, pushing the join condition into the sub‑query reduces work. The original query joins a large my_resources aggregation with a sub‑query that is executed multiple times. Using a WITH clause to materialize the sub‑query once and then joining eliminates redundant scans, cutting runtime from seconds to milliseconds.

Conclusion

The MySQL optimizer generates execution plans, but it is not perfect. Understanding its behavior—especially how it handles LIMIT offsets, type conversion, sub‑query materialization, and predicate push‑down—allows developers to write SQL that avoids common pitfalls and achieves high performance.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

mysqlindexesJOINquery-performanceSQL OptimizationWITHCondition Pushdown
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.