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

SQL-子查询和外部表之间的关系,sql,subquery,correlated-subquery,derived-table,inline-view,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)

问题

我需要更好地理解关于何时可以在子查询中引用外部表以及何时(以及为什么)这是一个不合适的请求的规则。我在试图重构的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
不幸的是,表2有时有重复的记录,所以我需要先聚合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中子句执行顺序的奇妙描述:()。我正在使用Oracle,但我认为这将是全面的标准。对子句求值有一个明确的顺序(从第一个开始),因此我认为任何出现在列表后面的子句都可以访问以前处理过的所有信息。我只能假设我的第二个查询以某种方式改变了顺序,以至于我的子查询被计算得太早了

  • 此外,我还发现了一个类似的问题( )但是,尽管投入很好,他们从未真正解释为什么他不能做他正在做的事情,只是给出了解决问题的替代方案。我尝试过他们的替代解决方案,但这给我带来了其他问题。也就是说,带有日期引用的子查询是整个操作的基础,所以我无法摆脱它

问题

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

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

提前谢谢

----编辑---

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

----更新---


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

以下问题如何:

SELECT t1.* FROM 
(
  SELECT * 
  FROM 
  (
    SELECT t2.id,
    RANK() OVER (PARTITION BY t2.id, t2.date ORDER BY t2.date DESC) AS R  
    FROM table2 t2
  )
  WHERE R = 1
) sub 
INNER JOIN table1 t1 
ON t1.id = sub.id

在第二个示例中,您试图将t1引用向下传递两个级别。。你不能这样做,你只能通过一级(这就是为什么第一级有效)。如果你能给出一个更好的例子来说明你想做什么,我们也可以帮助你重写你的查询。

所以我根据Martin Smith在上面的评论(谢谢Martin!)得出了这个结论,我想确保我将我的发现分享给任何遇到这个问题的人

技术注意事项

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

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

另一方面,我的第二句话是使用Oracle中称为内联视图的内容,也就是SQL Server中称为派生表的内容:

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

为什么我做的事很愚蠢

在第二个表中,我试图做的基本上是基于对另一个表的模糊引用创建一个视图,该表不在我的语句范围内。这就像试图引用一个表中的字段,而您在查询中并没有显式地声明该字段

解决方法

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

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

最后,我将重新评估我的整个查询方法