知识卡片
相关子查询到IN子查询的变换法则及适用边界
内容
用[[全称量词化的系统化SQL映射步骤]]推导出的SQL表达式常常包含相关子查询
(子查询内部WHERE条件引用外层表的列,如SPX.SNO=SX.SNO)——从性能角度看,
相关子查询原则上要对外层表的每一行都重新计算一遍,而不是像非相关子查询
那样只需整体计算一次,所以消除相关子查询往往值得研究。存在一条具体可用
的变换法则:SELECT sic FROM T1 WHERE[NOT]EXISTS(SELECT * FROM T2
WHERE T2.C=T1.C AND bx)可以改写成SELECT sic FROM T1 WHERE T1.C[NOT]
IN(SELECT T2.C FROM T2 WHERE bx)——后者的子查询不再引用外层表T1,是一个
真正独立、可以单独求值一次的子查询。书中提醒这条法则值得在能用的地方尽量
应用(理想情况下优化器应该能自动完成这一步,但现实中不能总指望优化器做到
最优),但它并非总能生效:一是[[NULL让逻辑等价的SQL改写产生不同的错误
答案]]里已证明的NULL陷阱(NOT IN在含NULL的候选集合上会产生和NOT EXISTS
不同的结果);二是即使完全避免了NULL,仍然存在这条变换根本无法应用的
情况——判断某个具体的相关子查询是否落在这条法则的适用范围内,需要针对
每个具体查询单独核实,不能把这条变换当成万能的机械替换规则来无脑套用。
参考来源
- 位置:《SQL与关系数据库理论——如何编写健壮的SQL代码》第11章"使用逻辑
表述SQL表达式"11.5节"例4:相关子查询"(源文件:OEBPS/text00123.html)
- 结论依据:原文明确"相关子查询必须对外层表的每一行进行重复计算,而不是
对于所有的行仅计算一遍。所以,消除相关子查询的可能性似乎是值得研究的
……SELECT sic FROM T1 WHERE[NOT]EXISTS(SELECT*FROM T2 WHERE T2.C=
T1.C AND bx)可以变换为SELECT sic FROM T1 WHERE T1.C[NOT]IN(SELECT
T2.C FROM T2 WHERE bx)……此变换也有很多无法应用的场合……null就可以是
原因之一……即使是避免了null,还是有不能应用此变换的情况"。
- 原始内容:相关子查询必须对外层表的每一行进行重复计算……可以变换为
SELECT sic FROM T1 WHERE T1.C[NOT]IN(SELECT T2.C FROM T2 WHERE bx)
……此变换也有很多无法应用的场合……即使是避免了null,还是有不能应用此
变换的情况。