有同学反馈昨天的《如何优化SQL表达式?》太长了,所以我临时撤下别的选题(恰又发现我用的Markdown编辑器的一个BUG,不支持三层“•”,估计原来用了三层“•”的地方文本都丢了),连夜缩写如下:(所有表格或代码块均可左右滚动)
我们有没有遇到过这样的情况:同样的查询,别人的代码瞬间出结果,我们的却要卡半天?实则,有时,SQL性能的差距往往藏在那些看似不起眼的SQL表达式里。
简单来说,SQL表达式就像数据库的“计算公式”,如:查询条件、计算逻辑等等。这些表达式写得好不好,直接决定了数据库要做多少“无用功”。重复计算、索引用不上、类型不匹配……这些小问题累积起来,就会让查询速度大打折扣。
SQL表达式优化并不复杂,只要掌握几个基础优化原则就能让我们少走许多弯路:列如别在索引列上随意用函数,避免让数据库反复计算同一个值,处理好空值和类型转换的坑。无论是日常办公的简单查询,还是支撑业务的复杂报表,优化SQL表达式都能帮我们节省时间、减少服务器压力。
接下来,就让我们一起来看看如何通过简单调整,就能让我们的SQL从“能跑”变成“跑得快”,进而,一步步不“修仙”也能得道。
一、基础优化原则
|
原则 |
说明 |
工具支持 |
示例 |
|
减少计算量 |
消除重复计算、常量折叠、拆分嵌套函数,避免运行时重复求值 |
CTE、子查询、变量、生成列 |
优化前:WHERE a*2 > 10 AND a*2 < 20 |
|
提升索引利用率 |
避免在索引列上使用函数或表达式,防止索引失效;可创建表达式索引补偿 |
EXPLAIN 、ANALYZE、索引设计工具 |
优化前:WHERE YEAR(created_at) = 2023(全表扫描) |
|
简化逻辑结构 |
合理组织CASE顺序(高频分支前置),优先使用简洁函数替代冗长条件 |
代码审查、执行计划分析 |
优化前: |
|
确保类型一致 |
避免隐式类型转换导致索引失效或性能下降,尤其是字符串与数字、日期比较 |
类型检查工具、SQL linter |
优化前:WHERE id = '123'(若id为INT,则触发隐式转换,可能不走索引) |
|
善用数据库特性 |
利用表达式索引、虚拟列(生成列)、函数索引等高级特性固化复杂逻辑 |
PostgreSQL、MySQL 8.0+、Oracle、SQL Server |
示例: |
二、中级优化方法
|
优化方法 |
适用场景 |
是否影响索引 |
示例 |
|
布尔短路优化 |
多条件过滤,尤其含昂贵函数(如:正则、JSON解析) |
⚠️ 依赖优化器实现 |
优化前:WHERE expensive_func(a) AND b > 10 |
|
避免标量子查询 |
主查询每行都执行一次子查询,性能极差 |
✅ 显著提升 |
优化前: |
|
处理NULL参与运算 |
数值表达式中字段可能为NULL,导致结果为NULL |
❌ 否 |
优化前:SELECT price * quantity FROM items(若任一为NULL,结果为NULL) |
|
位运算替代状态判断 |
状态字段使用位掩码(如:权限、标签组合) |
✅ 可索引(若索引支持表达式) |
优化前: |
|
正则替代LIKE OR |
多模式文本匹配,传统LIKE效率低 |
⚠️ 可结合文本索引(如:GIN) |
优化前: |
|
移除冗余DISTINCT |
在已唯一字段上误用DISTINCT,增加排序开销 |
✅ 减少排序与去重 |
优化前: |
|
使用NULLIF防除零 |
安全进行除法运算,避免运行时错误 |
❌ 否 |
优化前:SELECT total / quantity FROM sales(quantity=0时报错) |
三、高阶优化技巧
|
优化方法 |
说明 |
示例 |
|
避免JSON解析函数 |
频繁解析JSON字段(如:->>)无法利用索引,性能差 |
优化前: |
|
惰性求值优化 |
利用布尔短路机制,延迟执行高成本表达式 |
WHERE (a > 1000 OR (a < 100 AND expensive_func(b))) ✅ 当a > 1000为真时,跳过expensive_func(b) |
|
布尔表达式替代CASE |
简化简单二元判断,减少函数调用开销 |
优化前: |
|
优化日期间隔计算 |
避免在列上加减时间导致索引失效 |
优化前: |
|
利用生成列 |
将复杂表达式固化为列,支持索引与查询加速 |
ALTER TABLE products ADD COLUMN total_price NUMERIC ✅ 支持MySQL、PostgreSQL、Oracle、SQL Server |
|
向量化友善表达式 |
面向列式存储(如:ClickHouse、Snowflake)优化 |
避免: |
四、前沿优化思想
|
优化维度 |
关键策略 |
示例 |
|
短路求值与布尔代数 |
化简复杂条件表达式,降低计算复杂度 |
WHERE (a OR b) AND (a OR c) 等价于:WHERE a OR (b AND c) |
|
处理浮点精度问题 |
浮点数比较存在精度误差,应使用准确类型 |
优化前: |
|
表达式统计信息 |
协助优化器生成更优执行计划 |
PostgreSQL中执行 ANALYZE tbl; |
|
分布式表达式下推 |
在分布式数据库中,将过滤、计算下推到数据节点 |
示例: |
|
函数确定性 |
使用IMMUTABLE函数提升缓存与并行能力 |
定义函数时指定: |
|
SQL宏与模板 |
统一高频复杂表达式,提升可维护性 |
Oracle 23c+: |
五、优化检查清单
- [ ] 是否存在重复计算?→ 用CTE或变量缓存
- [ ] 是否在索引列上使用函数?→ 改写为列与常量比较
- [ ] 是否有隐式类型转换?→ 确保类型一致(尤其字符串与数字)
- [ ] 是否使用了标量子查询?→ 用窗口函数、JOIN或LATERAL替代
- [ ] 是否有NULL参与运算?→ 使用COALESCE或NULLIF安全处理
- [ ] 是否频繁解析JSON/XML?→ 使用生成列+索引固化路径
- [ ] 是否可用布尔表达式替代CASE WHEN?→ 提升执行效率(简单场景)
- [ ] 日期计算是否可参数化?→ 避免运行时函数调用影响索引
- [ ] 复杂表达式是否可物化?→ 使用生成列(GENERATED COLUMN)
- [ ] 是否适用于列式数据库?→ 优先使用向量化聚合函数(如:SUMIF)
- [ ] 是否使用了随机函数?→ 确保一致性,避免重复调用(如:RAND())
- [ ] 是否可通过表达式索引加速?→ 创建FUNCTIONAL INDEX
- [ ] 布尔条件顺序是否合理?→ 高选择性、低成本条件前置
- [ ] 是否存在除零风险?→ 使用NULLIF处理分母
- [ ] 多状态判断是否可用位运算?→ 适合状态掩码场景(权限、标签)
- [ ] 文本匹配是否可改用正则或全文检索?→ 复杂模式更高效
- [ ] DISTINCT是否必要?→ 避免冗余排序(主键/唯一字段无需)
- [ ] 分布式环境下表达式是否下推?→ 减少数据传输与中心负载
- [ ] 是否可使用物化视图?→ 对复杂聚合表达式进行预计算
- [ ] 是否启用查询重写或自动索引提议?→ 利用数据库内置优化工具(如:SQL Server DTA、Oracle SQL Tuning Advisor)
六、优化的三层境界
|
境界 |
特征 |
典型行为 |
|
初级 |
语法正确 |
写出能运行的SQL表达式,不关心性能 |
|
中级 |
性能优化 |
合理使用索引、避免函数调用、优化条件顺序、消除标量子查询 |
|
高级 |
语义重构 |
等价逻辑转换、使用生成列、表达式索引、分布式下推策略 |
|
超高级 |
哲学思考 |
延迟计算、无副作用设计、编译执行优化、SQL宏抽象、与数据建模协同演进 |
说明:
延迟计算:仅在真正需要时才求值(如:CTE的非物化、窗口函数延迟)
无副作用:避免VOLATILE函数、随机数、会话状态依赖
编译优化:现代数据库(如:Oracle、SQL Server)具备JIT编译能力,可将SQL表达式编译为机器码执行
七、终极理念
SQL表达式优化的本质是:通过更精准的语义表达,减少计算量,提升执行效率,同时保证代码的可维护性与系统的可扩展性。
我们必须清楚,优化不是一次性的任务,而是一个持续迭代的过程,需要结合:
- 业务语义(何时为空?精度要求?)
- 数据特征(基数、分布、倾斜度)
- 数据库特性(优化器能力、索引类型、执行引擎)
- 架构模式(单机、分布式、列式、HTAP)
只有我们综合运用上述原则与技巧,方能实现数据库从“能运行”到“高性能、高可维护”的跃迁。
结语:
最好的优化,是让数据库“少做一点事”。
最优雅的SQL,是让逻辑清晰、执行高效、未来可期。



收藏了,感谢分享