sql存储函数仅用于计算,不可执行dml或调用副作用操作;应避免在where/join中滥用,正确标注确定性级别,并将复杂逻辑拆解或物化。

SQL 存储函数本身不支持直接修改数据库状态(如 INSERT、UPDATE、DELETE),也不能调用含副作用的存储过程,这是它和存储过程最根本的区别。想靠函数“顺便”更新数据或提升性能,往往事与愿违——用错场景反而拖慢查询。
明确函数适用边界:只做计算,不碰数据
存储函数设计初衷是封装可复用的标量逻辑,比如格式化日期、计算折扣率、拼接姓名等。一旦在函数体内写入 DML 语句,多数数据库(如 MySQL)会报错;即便 PostgreSQL 允许带 volatile 属性的函数执行 DML,也会导致查询计划失效、无法内联、统计信息失真等问题。
- ✅ 正确做法:把价格打 95 折 → RETURN price * 0.95;
- ❌ 错误做法:在函数里记录某次调用日志 → 触发 INSERT,破坏函数纯度
- ⚠️ 替代方案:需写日志或更新状态,改用存储过程 + 应用层显式调用
避免在 WHERE 或 JOIN 条件中调用复杂函数
如果 WHERE 子句写成 WHERE UPPER(name) = 'JOHN',数据库通常无法使用 name 字段上的普通索引(除非支持函数索引且已创建)。每次扫描都要对每行执行一次函数,数据量大时开销陡增。
95Shop可以免费下载使用,是一款仿醉品商城网店系统,内置SEO优化,具有模块丰富、管理简洁直观,操作易用等特点,系统功能完整,运行速度较快,采用ASP.NET(C#)技术开发,配合SQL Serve2000数据库存储数据,运行环境为微软ASP.NET 2.0。95Shop官方网站定期开发新功能和维护升级。可以放心使用! 安装运行方法 1、下载软件压缩包; 2、将下载的软件压缩包解压缩,得到we
- 优先让函数作用于常量侧:改为 WHERE name = LOWER('JOHN')(假设数据存小写)
- 确需函数匹配时,建函数索引(MySQL 8.0+、PostgreSQL、Oracle 均支持):CREATE INDEX idx_name_upper ON users (UPPER(name));
- JOIN 中慎用:如 ON f1.calc_hash(u.id) = f2.get_hash(o.user_id),很可能迫使全表扫描 + 哈希计算,不如提前物化哈希值到字段
用确定性(DETERMINISTIC)标记提升优化器信心
MySQL 要求函数声明是否 deterministic;PostgreSQL 用 STABLE / IMMUTABLE。正确标注能让优化器做更多推断:比如将函数调用上提、合并重复调用、甚至预计算。
- 返回值只依赖输入参数,且不读数据库 → 标 IMMUTABLE(如 date_part('year', t))
- 不修改数据、但可能因当前事务状态返回不同结果(如 CURRENT_TIMESTAMP)→ 标 STABLE
- 读取表数据或调用随机函数 → 只能标 VOLATILE,优化器基本放弃优化
- 未标注或标错 → 优化器按最保守策略处理,性能打折
复杂逻辑别硬塞进函数:拆解 + 物化更高效
一个函数里嵌套 5 层子查询、多次 CASE、跨表关联?这已超出函数职责。执行时每次调用都重新解析、编译、执行完整逻辑,无法复用执行计划,还容易阻塞并发。
- 把高频中间结果存为视图或物化视图(PostgreSQL 可用 REFRESH MATERIALIZED VIEW CONCURRENTLY)
- 将多步骤业务规则拆成多个小函数,各自专注单一转换,便于测试和组合
- 考虑用 CTE 替代嵌套函数调用,让优化器看到整体结构,更好选择连接顺序和索引
- 极端情况(如实时风控评分),函数只是入口,核心计算移至应用层或专用引擎(如 Flink、Redis 模块)
函数不是万能胶,而是精准螺丝刀。用对位置,省力又可靠;拧错地方,不仅打滑,还可能崩刃。










