TSQL中能否通过单次子查询同时更新两列,避免重复调用子查询?
问题解答
语法支持场景下可以直接使用你给出的写法
PostgreSQL、Oracle、SQLite 3.33及以上版本等主流数据库都支持UPDATE语句的行值构造器赋值语法,你预期的第一种写法是完全合法可用的:
-- Table D contains a Date and a ProcessId UPDATE T1 SET (Val1, Val2) = ( SELECT SUM(Val1A), SUM(Val2A) --Val1A and Val2A are equations and are not taken from just one table but multiple FROM D INNER JOIN T8 ON T8.ProcessId = D.ProcessId INNER JOIN T4 ON T4.Id = T8.T4Id INNER JOIN T6 ON T4.Id = T6.T4Id WHERE D.Date <= @ProcessDate AND T6.T4Id = T1.T4Id) FROM T1 INNER JOIN T4 ON T4.Id = T1.T4Id INNER JOIN T5 ON T5.Id = T1.T5Id INNER JOIN T6 ON T4.Id = T6.T4Id INNER JOIN T7 ON T7.Id = T4.T7Id
这种写法下子查询仅会执行一次,返回的两个聚合结果会分别赋值给Val1和Val2,不需要重复调用子查询,完全符合你的需求。
不支持行值语法的数据库可以用CTE/派生表方案
如果使用的是不支持上述行赋值语法的数据库(比如低版本SQL Server),可以通过公共表表达式(CTE)或者派生表预计算需要更新的字段值,再通过关联更新完成赋值,同样只需要执行一次复杂查询逻辑,不需要重复写子查询,示例写法如下:
WITH AggregatedValues AS ( SELECT T6.T4Id, SUM(Val1A) AS CalcVal1, SUM(Val2A) AS CalcVal2 FROM D INNER JOIN T8 ON T8.ProcessId = D.ProcessId INNER JOIN T4 ON T4.Id = T8.T4Id INNER JOIN T6 ON T4.Id = T6.T4Id WHERE D.Date <= @ProcessDate GROUP BY T6.T4Id ) UPDATE T1 SET Val1 = av.CalcVal1, Val2 = av.CalcVal2 FROM T1 INNER JOIN T4 ON T4.Id = T1.T4Id INNER JOIN T5 ON T5.Id = T1.T5Id INNER JOIN T6 ON T4.Id = T6.T4Id INNER JOIN T7 ON T7.Id = T4.T7Id INNER JOIN AggregatedValues av ON av.T4Id = T1.T4Id
该方案优势:
- 复杂的关联、聚合逻辑仅需要编写、执行一次,维护成本更低
- 避免重复执行相同的查询逻辑,性能优于重复调用子查询的写法
- 语法兼容性更广,绝大多数支持CTE的数据库都可以使用
注意事项
你需要保证预计算的结果和T1表的待更新行是一对一关联的,避免出现一对多匹配导致更新结果不符合预期,必要时可以增加关联条件或去重逻辑保证匹配唯一性。
内容的提问来源于stack exchange,提问作者PancakeAlchemist
相关产品推荐
相关产品推荐

