30 Essential SQL Query Optimization Techniques
This article presents thirty practical SQL optimization tips, covering index usage, avoiding full‑table scans caused by operators like !=, NULL checks, OR, LIKE, IN, functions, and expressions, as well as best practices for temporary tables, cursors, and transaction size to improve database performance.
1. Index columns used in WHERE and ORDER BY
Avoid full‑table scans by creating indexes on columns referenced in WHERE and ORDER BY clauses.
2. Do not use != or <> in WHERE
These operators cause the engine to ignore indexes and perform a full scan.
3. Avoid NULL checks in WHERE
NULL comparisons also prevent index usage. Example: select id from t where num is null Set a default value (e.g., 0) and rewrite as:
select id from t where num = 04. Avoid OR in WHERE
OR leads to full scans. Replace with UNION ALL:
select id from t where num = 10
union all
select id from t where num = 205. LIKE with leading wildcard causes full scans
Query like name like '%abc%' triggers a full scan; consider full‑text search instead.
6. Use IN/NOT IN cautiously
These can cause full scans. Prefer BETWEEN for continuous ranges:
select id from t where num between 1 and 37. Parameter values in WHERE may prevent index use
Because the optimizer cannot know the value at compile time, it may choose a full scan. Force index usage:
select id from t with(index(索引名)) where num = @num8. Do not apply arithmetic expressions on indexed columns
Expression num/2 = 100 disables index; rewrite as num = 200:
select id from t where num = 100*29. Avoid functions on indexed columns
Functions like substring(name,1,3)='abc' or datediff(day,createdate,'2005-11-30')=0 prevent index use. Replace with range conditions:
select id from t where name like 'abc%'
select id from t where createdate >= '2005-11-30' and createdate < '2005-12-01'10. Do not place functions or arithmetic on the left side of =
Such expressions may stop the optimizer from using indexes.
11. Use the leftmost column of a composite index
When a composite index is defined, the query must filter on its first column to be usable; keep column order consistent with the index.
12. Avoid meaningless queries that return no rows
Example that consumes resources: select col1,col2 into #t from t where 1=0 Replace with a proper table definition:
create table #t(...)13. Prefer EXISTS over IN
Rewrite:
select num from a where exists (select 1 from b where num = a.num)14. Indexes are ineffective on low‑cardinality columns
If a column (e.g., gender) has many duplicate values, an index may not improve query speed.
15. Limit the number of indexes per table
Too many indexes (more than six) can degrade INSERT/UPDATE performance; add indexes only when truly needed.
16. Avoid updating clustered index columns frequently
Changing clustered key values forces row reordering and heavy resource consumption; consider a non‑clustered key if updates are frequent.
17. Prefer numeric over character columns for numeric data
Numeric comparisons are faster and use less storage than string comparisons.
18. Use VARCHAR/NVARCHAR instead of CHAR/NCHAR
Variable‑length types save space and improve search efficiency.
19. Never use SELECT *
Specify required columns to avoid returning unnecessary data.
20. Prefer table variables over temporary tables
Table variables have limited indexing (only primary key) but can be more efficient for small datasets.
21. Reduce creation/deletion of temporary tables
Frequent temp‑table churn consumes system resources.
22. Use temporary tables judiciously
They are useful for repeated access to large result sets, but for one‑off operations a derived table may be better.
23. For bulk inserts into a new temp table, use SELECT‑INTO
When inserting a large amount of data at once, SELECT … INTO avoids excessive logging; for small data, create the table first then INSERT.
24. Explicitly drop temporary tables at the end of a stored procedure
TRUNCATE then DROP to release locks promptly.
25. Avoid cursors for large rowsets
Cursors are slow; if processing exceeds 10,000 rows, rewrite using set‑based logic.
26. Seek set‑based solutions before cursor or temp‑table approaches
Set‑based queries are generally more efficient.
27. When necessary, use FAST_FORWARD cursors for small datasets
They can outperform row‑by‑row processing for limited data.
28. Set SET NOCOUNT ON at the start of stored procedures and triggers
Suppresses unnecessary DONE_IN_PROC messages, reducing network traffic.
29. Limit result set size sent to clients
Large result sets should be evaluated for necessity to avoid overwhelming clients.
30. Keep transactions short
Shorter transactions improve concurrency and reduce lock contention.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
