SQL - 子查询和外表之间的关系

标签 sql subquery correlated-subquery derived-table 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

不幸的是,table2 有时有重复的记录,因此我需要先聚合 t2,然后再将其连接到 t1。然而,当我尝试将其包装在子查询中来完成此操作时,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

我知道这些是根本不同的查询,我要求编译器将它们放在一起,但我不明白为什么一个可以工作,而另一个则不行。

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

有用的引用资料

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

  • 此外,我发现有人提出了类似的问题( Referencing outer query's tables in a subquery )但是,虽然输入很好,但他们从未真正解释为什么他不能做他正在做的事情,只是为他的问题提供了替代解决方案。我已经尝试过他们的替代解决方案,但这给我带来了其他问题。也就是说,带有日期引用的子查询是整个操作的基础,因此我无法摆脱它。

问题

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

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

提前致谢!

------编辑------

正如一些人猜测的那样,上面的这些查询并不是我正在重构的实际查询,而是我遇到的问题的一个示例。我正在处理的查询要复杂得多,因此我犹豫是否将其发布在这里,因为我担心它会让人们偏离轨道。

------更新------

所以我由一位开发人员同事运行了这个,他对为什么我的子查询无法访问 t1 有一个可能的解释。因为我将此子查询括在括号中,所以他认为在评估我的表 t1 之前正在评估此子查询。这肯定可以解释我收到的“ORA-00904:“t1”。“id”:无效标识符”错误。它还表明,就像运算的算术顺序一样,向语句添加括号可以使其在某些子句评估中具有优先级。如果专家同意/不同意这是对我在这里看到的内容的合乎逻辑的解释,我仍然希望专家能够发表意见。

最佳答案

所以我根据马丁·史密斯上面的评论得出了这个结论(谢谢马丁!),我想确保我与遇到这个问题的其他人分享我的发现。

技术考虑

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

这实际上是一种相当低效的拉回数据的方法,因为它会为外表中的每一行重新运行子查询。因此,我将寻找在代码中消除这些类型的子查询的方法:

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

内联 View /派生表在查询开始时创建一个临时的未命名 View ,然后将其视为另一个表,直到操作完成。由于编译器在 FROM 行上看到这些子查询时需要创建一个临时 View ,因此这些子查询必须完全独立,子查询之外没有任何引用。

为什么我所做的事情是愚蠢的

我在第二个表中尝试做的事情本质上是基于对另一个表的模糊引用创建一个 View ,该引用超出了我的语句的知识范围。这就像尝试引用表中您未在查询中明确说明的字段一样。

解决方法

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

同样,Oracle 中也可以通过不同的语法使用此功能:

最终,我将重新评估我对这个查询的整个方法,这意味着我必须从头开始重建它(无论你相信与否,我最初并没有创造这个怪物 - 我发誓!)。 非常感谢所有发表评论的人 - 这确实难倒了我,但所有的意见都帮助我走上了正确的道路!

关于SQL - 子查询和外表之间的关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20106632/

相关文章:

sql - 有没有办法从时间戳类型列获取 DateTime 值?

mysql - rails 3 : How to find records with field possibly equals to nil?

sql - Microsoft SQL Server Management Studio - 尝试添加 FK 约束,出现错误 : "Invalid column..."

mysql - (MySQL) 使用在更新查询中计算的值更新多行

sql - PostgreSQL:为什么作为表达式的子查询不能返回多行,而函数可以?

mysql - SQL子集查询

mysql - 将 SELECT 中的相关子查询重写为 JOIN 语句

php - 如何将动态参数传递给查询?

Mysql查询两个表与引用表连接

sql - 如何在我的子选择中删除相关子查询(由于 presto 限制)