知识卡片

多重赋值:解决"延迟检查"困境的更优方案

普通读书笔记卡

内容

“供应商S1和零件P1必须在同一城市”这类约束,会遇到一个具体的立即检查困境: 如果要把S1和P1都改到某个新城市,两条分开的UPDATE语句在立即检查下,第一条 执行完(改了S1)、第二条还没执行(P1还在旧城市)时,中间状态必然违反约束, 导致第一条UPDATE就失败。传统解决方案是把约束标记为DEFERRABLE,延迟到COMMIT 时刻才检查,让两条UPDATE语句共处同一事务、中间容忍暂时不一致——但这正是 [[ACID一致性不等于正确性及事务不是完整性单位]]和[[数据库约束必须立即检查的 理由与语义优化]]反对的做法:如果两条UPDATE之间去问事务”S1和P1现在在不同 城市吗”,得到的答案会是”是的”,这个不一致完全是真实存在、可被观察到的。 更好的方案是支持多重赋值(multiple assignment):允许任意数量的单独赋值 “同时”作为同一条语句的组成部分执行,比如Tutorial D里用逗号连接两个UPDATE (S:=...,P:=...;)。多重赋值的语义分两步:先计算等号右边所有源表达式的值, 再统一执行左边所有变量的赋值——因为所有源表达式都是基于赋值前的状态计算的, 各个单独赋值之间互不依赖,执行顺序无关紧要,可以视为并行/同时发生;更关键 的是,因为多重赋值在语义上是一个原子运算,赋值”中途”根本不存在可被观察的 状态,所以完整性检查不会、也不需要在赋值过程中途介入——这样,两条本来分开 会失败的UPDATE,打包成一次多重赋值后就能直接成功,而且完全不需要引入延迟 检查这个机制。SQL对多重赋值有一些零散的隐式支持(级联删除、连接视图更新、 FETCH/SELECT INTO、行赋值),但缺少对”多个不同表同时赋值”这种显式多重赋值 的直接支持,这恰好是绕开延迟检查所必需的那种能力,书中期望这会在SQL标准 未来版本里得到补齐。

参考来源

- 位置:《SQL与关系数据库理论——如何编写健壮的SQL代码》第8章"SQL与约束" 8.7节"不是有些检查必须延迟进行吗"(源文件:OEBPS/text00095.html) - 结论依据:原文明确"如果在两个UPDATE语句之间……询问事务……那么得到的答案 会是'是的'……解决上述问题的更好方案是支持多重形式的赋值,即允许任意数量 的单独赋值'同时'执行……因为多重赋值在语义上被认为是原子运算,所以不会在 此类赋值的'中途'执行完整性检查……现在根本不需要延迟检查了""SQL当前不 支持的一类多重赋值是对于多个不同表的显式'同时'赋值"。 - 原始内容:解决上述问题的更好方案是支持多重形式的赋值……因为多重赋值在 语义上被认为是原子运算,所以不会在此类赋值的"中途"执行完整性检查……现在 根本不需要延迟检查了。