当前位置: 首页 > 面试题库 >

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

濮阳唯
2023-03-14
问题内容

问题

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

以下语句可以正常工作:

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与WHERE子句)。我使用的是Oracle,但我认为这将是全面的标准。子句评估有明确的顺序(首先是FROM),因此我认为任何出现在列表后面的子句都可以访问以前处理过的所有信息。我只能假设我的第二个查询以某种方式改变了顺序,以至于我的子查询被评估为时过早?

  • 另外,我发现了一个类似的问题(在子查询中引用外部查询的表 ),但是尽管输入很好,但是他们从来没有真正解释过为什么他不能做自己所做的事情,而只是给出了解决问题的替代方案。我已经尝试过他们的替代解决方案,但这给我带来了其他问题。即,带有日期引用的子查询是整个操作的基础,因此我无法摆脱它。

问题

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

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

提前致谢!

- - - 编辑 - - -

正如一些人推测的那样,以上这些查询不是我正在重构的实际查询,而是我所遇到的问题的一个示例。我正在使用的查询要复杂得多,因此我很犹豫将其发布在这里,因为我担心它会使人们误入歧途。

- - - 更新 - - -

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


问题答案:

因此,我根据马丁·史密斯(Martin Smith)在上面所做的评论(谢谢马丁!)来弄清了这一点,并希望确保将我的发现分享给其他遇到此问题的人。

技术注意事项

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

  • http://en.wikipedia.org/wiki/Correlated_subquery
  • http://www.programmerinterview.com/index.php/database-sql/correlated-vs-uncorrelated-subquery/

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

  • https://blogs.oracle.com/optimizer/entry/optimizer_transformations_subquery_unesting_part_1

另一方面,我的第二条语句使用的是Oracle中的 内联视图 ,在SQL Server中也称为 派生表

  • http://docs.oracle.com/cd/B19306_01/server.102/b14200/queries007.htm
  • http://www.programmerinterview.com/index.php/database-sql/derived-table-vs-subquery/

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

为什么我在做的事很愚蠢

我在第二张表中试图做的基本上是基于对另一个表的不明确引用创建一个视图,该表超出了我的陈述范围。这就像试图引用您在查询中未明确说明的表中的字段一样。

解决方法

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

  • http://technet.microsoft.com/zh-CN/library/ms175156(v=SQL.105).aspx

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

  • Oracle中SQL Server APPLY的等效项是什么?

最终,我将重新评估该查询的整个方法,这意味着我将不得不从头开始重建它(信不信由你,我最初并没有创造出这种怪异的东西-我发誓!)。
非常感谢所有发表评论的人 -这肯定让我很沮丧,但是所有的投入都使我走上了正确的道路!



 类似资料:
  • 问题内容: 对于SQL Server 2000_ 对于给定的表“ foo”,我需要一个查询来生成一组表,这些表具有指向foo的外键。 问题答案: SELECT o2.name FROM sysobjects o INNER JOIN sysforeignkeys fk on o.id = fk.rkeyid INNER JOIN sysobjects o2 on fk.fkeyid = o2.id

  • 我有两个联合查询,如下所示: 现在,我想在另一个查询中使用此联合。 当我运行它时,我得到ORA-00904:"P"."CUSTOMER_NO":无效的标识符。我需要将h1.customer_no加入到外部查询customer_no。 我看到过一些带有rank的查询,但我不太明白。如何将内部查询与外部查询连接起来? 提前感谢。

  • 问题内容: 是否可以在使用MySQL的子查询中引用外部查询?我知道在 某些 情况下这是可能的: 但是我想知道这样的事情是否可以工作: 我知道我可以使用或通过将外部子句拉入子查询来实现相同目的,但是我需要这样做来自动生成SQL,并且由于各种其他原因,不能使用任何一种替代方法。 更新 :对不起,这个问题引起了一些混乱:第一个查询只是一个可行的示例,以演示我 不需要的 东西。 更新2 :我需要两个u.i

  • 我需要从中选择所有行,如果选择位置子句匹配,则从中选择匹配这是我的外部与子查询,但它失败了。有人可以帮忙吗?

  • 问题内容: 我目前正在编写更新语句,以使可查询表保持最新状态。这两个表之间的架构相同,内容也不重要: 我的更新语句如下所示: 需要注意的两件事是:1)更新结束时没有where子句(这可能是问题所在); 2)更新后的所有记录都具有相同的值。我的意思是: 我的问题是如何解决此问题,以使表正确地将登台的“新”数据反映为正确的SQL更新? 更新 因此,我的登台数据可以恰好反映了其中的内容,为了便于讨论,它

  • 问题内容: 我有两个表:表A和表B 表A和表B都有列。 表A和表B都有列。 表A的一列也称为。 检查条件: 如果表A“键” =恒定,则从表A检索RowId。 从中获取这些行ID,并检查这些行的字段是否>具有相同rowId的表B的> ModifiedAT字段。 Table没有重复的RowId,而Table具有。 我自己尝试的方法: 注意:另外,令我感到惊讶的是,如果我将硬编码值替换为“ 1”,它会起