SQL - SubQuery和外表之间的关系

Dan*_*anK 13 sql subquery derived-table correlated-subquery inline-view

问题

我需要更好地理解有关何时可以在子查询中引用外部表以及何时(以及为什么)这是不合适的请求的规则.我在Oracle SQL查询中发现了一个重复,我正在尝试重构,但是当我尝试将我的引用表转换为分组子查询时,我遇到了问题.

以下声明适用:

SELECT  t1.*  
FROM    table1 t1, 
INNER JOIN table2 t2 
        on t1.id = t2.id        
        and t2.date = (SELECT max(date) 
                       FROM   table2  
                       WHERE  id = t1.id) --This subquery has access to t1
Run Code Online (Sandbox Code Playgroud)

不幸的是,table2有时会有重复的记录,所以在将它加入t1之前我需要首先聚合t2.但是,当我尝试将其包装在子查询中以完成此操作时,SQL引擎突然无法再识别外部表.

SELECT  t1.* 
FROM    table1 t1, 
INNER JOIN (SELECT * 
            FROM  table2 t2
            WHERE t1.id = t2.id              --This loses access to t1
              and t2.date = (SELECT max(date) 
                             FROM   table2 
                             WHERE  id = t1.id)) sub on t1.id = sub.id 
                             --Subquery loses access to t1
Run Code Online (Sandbox Code Playgroud)

我知道这些是根本不同的查询,我要求编译器放在一起,但我不知道为什么一个会工作而不是另一个.

我知道我可以复制子查询中的表引用,并有效地将子查询从外部表中分离,但这似乎是完成此任务的一种非常难看的方式(所有代码和处理的重复都是如此).

有用的参考资料

  • 我发现了这个在SQL Server中执行子句的顺序的精彩描述:(INNER JOIN ON vs WHERE子句).我正在使用Oracle,但我认为这将是全面的标准.对于子句评估有一个明确的顺序(FROM是第一个),所以我认为在列表中进一步出现的任何子句都可以访问以前处理过的所有信息.我只能假设我的第二个查询以某种方式更改了这个排序,以便我的子查询过早评估?

  • 另外,我发现了一个类似的问题(在子查询中引用外部查询的表 )但是虽然输入很好,但他们从未真正解释过为什么他不能做他正在做的事情而只是为他的问题提供了替代解决方案.我已经尝试了他们的替代解决方案,但它引起了我的其他问题.也就是说,带有日期引用的子查询是整个操作的基础,所以我无法摆脱它.

问题

  • 我想了解我在这里做了什么...为什么我的初始子查询可以看到外部表但不是在我将整个语句包装在子查询中之后?

  • 也就是说,如果我要做的事情无法完成,那么重构第一个查询以消除重复的最佳方法是什么?我应该两次引用table1(需要所有重复)吗?或者是否(可能)有更好的方法来解决这个问题?

提前致谢!

- - - 编辑 - - -

正如一些人所推测的那样,上面的查询并不是我正在重构的实际查询,而是我遇到的问题的一个例子.我正在使用的查询要复杂得多,所以我不愿在这里发帖,因为我担心它会让人偏离轨道.

------ UPDATE ------

所以我由一位开发人员运行这个,他有一个可能的解释,为什么我的子查询失去了对t1的访问权限.因为我将这个子查询包装在括号中,所以他认为在我的表t1被评估之前正在评估这个子查询.这肯定会解释'ORA-00904:"t1"."id":我收到的无效标识符'错误.它还表明,与操作的算术顺序一样,将语句添加到语句中会使其在某些子句评估中具有优先级.如果他们同意/不同意这是我在这里看到的合乎逻辑的解释,我仍然希望专家能够权衡.

Dan*_*anK 6

所以我根据马丁史密斯在上面做出的评论(感谢马丁!)来解决这个问题,我想确保我和其他任何人一起分享我的发现.

技术考虑

首先,如果我使用适当的术语来描述我的问题,肯定会有所帮助:我上面的第一个语句使用了相关的子查询:

这实际上是一种相当低效的数据撤销方式,因为它重新运行外部表中每一行的子查询.出于这个原因,我将在我的代码中寻找消除这些类型的子查询的方法:

另一方面,我的第二个声明是在Oracle中使用所谓的内联视图,也称为SQL Server中的派生表:

内联视图/派生表在查询开头创建一个临时的未命名视图,然后将其视为另一个表,直到操作完成.因为编译器需要在FROM行上查看这些子查询时创建临时视图,所以这些子查询必须完全自包含且子查询外没有引用.

为什么我在做什么是愚蠢的

我在第二个表中尝试做的事实上是基于对我声明之外的另一个表的模糊引用创建一个视图.这就像尝试引用您在查询中未明确声明的表中的字段一样.

解决方法

最后,值得注意的是,马丁提出了一种相当聪明但最终效率低下的方法来完成我想要做的事情.Apply语句是一个专有的SQL Server函数,但它允许您与派生表之外的对象进行通信:

同样,Oracle可以通过不同的语法提供此功能:

最终,我将重新评估我对这个查询的整个方法,这意味着我将不得不从头开始重建它(不管你信不信,我最初没有创造这种怪物 - 我发誓!).非常感谢所有评论的人 - 这绝对让我很难过,但所有的投入都帮助我走上正轨!