SQL Server 2017 跨表同时更新两列仅单列生效问题
问题原因
SQL Server 多表关联更新的核心规则:当目标表的1条记录,通过JOIN匹配到源表多条记录时,更新操作只会从所有匹配的源表记录中选取任意一条作为赋值依据,不会遍历所有匹配行多次更新同一条目标记录,多次赋值的结果也不会叠加保留。
你写的语句中,Main表ID=42的单条记录和Val表JOIN后会生成2条匹配结果:
- 第一条:Type=1,Val=345.67
- 第二条:Type=2,Val=567.89
执行更新时数据库随机选中了Type=1的匹配行:此时Val1满足CASE判断条件被赋值为345.67,Val2不满足条件走ELSE分支保留原有NULL值,因此出现你看到的仅Val1更新成功的现象。如果执行时选中Type=2的行,就会出现仅Val2更新、Val1为NULL的反向结果,结果完全不可控。
单语句正确实现方法
需要先把Val表的多行数据按MainID聚合为单行的行转列结果,保证和Main表是1:1匹配关系后再做关联更新,以下两种写法都适配SQL Server 2017版本:
写法1:CASE聚合(通用兼容性最好)
UPDATE m SET m.Val1 = v.Val1, m.Val2 = v.Val2 FROM Main m INNER JOIN ( SELECT MainID, MAX(CASE WHEN Type = '1' THEN Val END) AS Val1, MAX(CASE WHEN Type = '2' THEN Val END) AS Val2 FROM Val GROUP BY MainID ) v ON m.ID = v.MainID WHERE m.ID = 42;
写法2:PIVOT行转列(语义更直观)
UPDATE m SET m.Val1 = v.[1], m.Val2 = v.[2] FROM Main m INNER JOIN ( SELECT MainID, [1], [2] FROM Val PIVOT( MAX(Val) FOR Type IN ([1],[2]) ) pvt ) v ON m.ID = v.MainID WHERE m.ID = 42;
注意:所有多表关联更新场景,都要保证JOIN后目标表和源表结果集是1:1匹配关系,不要依赖数据库对多匹配行的隐式选择逻辑,否则会出现随机更新的不可预期结果。
内容的提问来源于stack exchange,提问作者Volker
相关产品推荐
相关产品推荐

