设置计算列为PERSISTED的作用是什么?为何查询仍逐行触发函数?
关于PERSISTED计算列的作用及问题解析
PERSISTED计算列的实际作用
- 物理存储计算结果:和非持久化计算列每次查询动态计算不同,持久化计算列会把计算结果直接存在表中,只有当依赖的基础列数据变更时才会重新计算,能大幅降低重复计算的开销。
- 支持创建索引:可以直接在持久化计算列上创建非聚集索引,而非持久化计算列需要满足严格的确定性、精确性条件才能建索引。
- 自动维护:当关联的基础字段更新时,SQL Server会自动同步更新持久化计算列的存储值,无需手动干预。
为什么你的场景中仍会调用函数
问题出在你使用的dbo.fn_calcIncome()函数上:
- 函数非确定性:如果这个函数的返回值不是仅由输入参数决定(比如你的函数无参数,却依赖系统时间、其他表数据、会话配置等外部状态),SQL Server会判定它为非确定性函数。对于这类函数,即使你加了
PERSISTED关键字,数据库也无法持久化存储计算结果,只能在每次查询时重新调用函数计算。 - 函数非精确性:如果函数涉及浮点数等非精确运算,也可能导致
PERSISTED设置失效,SQL Server仍会动态计算。
你可以用以下语句检查函数的确定性:
SELECT OBJECT_NAME(object_id) AS function_name, is_deterministic FROM sys.sql_modules WHERE object_id = OBJECT_ID('dbo.fn_calcIncome')
如果返回的is_deterministic是0,就说明这是个非确定性函数,PERSISTED无法生效。
解决思路
- 调整函数为确定性函数:确保函数的返回值仅由输入参数决定,不依赖任何外部可变状态。如果函数需要使用当前表的字段,应该把这些字段作为参数传入函数,比如改成
dbo.fn_calcIncome(id, name),这样函数的计算逻辑完全依赖输入参数,就能成为确定性函数,PERSISTED也能正常生效。 - 改用触发器+普通列:如果函数逻辑必须依赖外部非确定状态,那没法用
PERSISTED计算列,此时可以新增一个普通列存储计算结果,然后用触发器在基础数据变更时自动调用函数更新这个列的值。
内容的提问来源于stack exchange,提问作者AngryHacker
相关产品推荐
相关产品推荐

