使用损坏的子选择查询应该导致错误但返回行

evi*_*ive 7 sql oracle subquery correlated-subquery

我不明白这种情况下的行为.根据我的理解,使用无效子查询的查询应该会导致错误.但在此示例中,它返回一些行.

测试数据:

create table test_values ( tst_id number, tst_id2 number, tst_value varchar2( 10 ) );

create table test_lookup ( tst_id number, tst_value varchar2( 10 ) );

insert into test_values( tst_id, tst_id2, tst_value ) values ( 1, 2, 'a' );
insert into test_values( tst_id, tst_id2, tst_value ) values ( 1, 2, 'b' );
insert into test_values( tst_id, tst_id2, tst_value ) values ( 2, 2,'c' );
insert into test_values( tst_id, tst_id2, tst_value ) values ( 2, 2,'d' );

insert into test_lookup( tst_id, tst_value ) values ( 1, 'findMe' );

commit;
Run Code Online (Sandbox Code Playgroud)

按预期工作:

select * from test_values where tst_id in ( select tst_id from test_lookup where tst_value = 'findMe' );

/*
    TST_ID    TST_ID2 TST_VALUE 
---------- ---------- ----------
         1          2 b         
         1          2 a   
*/

select tst_id2 from test_lookup where tst_value = 'findMe'; 
--ORA-00904: "TST_ID2": invalid identifier
Run Code Online (Sandbox Code Playgroud)

但是下面的查询也是检索行,显然是通过从"test_values"-table获取"test_id2"列而不是从子查询中所述的"test_lookup"-table,尽管不使用内部和外部的别名部分.

select * from test_values where tst_id in ( select tst_id2 from test_lookup where tst_value = 'findMe' );

/*
   TST_ID    TST_ID2 TST_VALUE  
---------- ---------- ----------
         2          2 c         
         2          2 d         
*/
Run Code Online (Sandbox Code Playgroud)

Bon*_*ist 8

原因是,当子查询中不存在未分类的列但外部查询中确实存在时,Oracle假定您引用外部查询中的列.

使用别名,您感到困惑的查询将如下所示:

select *
from   test_values tv
where  tv.tst_id in (select tv.tst_id2
                     from   test_lookup tl
                     where  tl.tst_value = 'findMe');
Run Code Online (Sandbox Code Playgroud)

希望这会让事情变得更清楚?

您所看到的问题是一个非常好的示例,说明为什么您应该始终使用它们来自哪个表来标记列 - 这样可以更容易地维护查询以便开始!

  • @evilive:这实际上并不是Oracle特有的.这就是定义名称解析在SQL标准中的工作方式 (2认同)

Ale*_*ole 5

当您的“损坏”查询用作子查询时,它仍然可以引用外部查询的表列;这是相关性工作所必需的。它正在tst_id2test_values表中拾取列。如果两个表具有相同的列,则内部表将优先,但这里的情况并非如此。

从文档

Oracle 通过查看子查询中命名的表,然后查看父语句中命名的表来解析子查询中的非限定列。

您可以通过添加表别名来查看发生了什么;这仍然错误:

select * from test_values tv where tst_id in (
  select tl.tst_id2 from test_lookup tl where tl.tst_value = 'findMe' );

ORA-00904: "TL"."TST_ID2": invalid identifier
Run Code Online (Sandbox Code Playgroud)

但这有效:

select * from test_values tv where tst_id in (
  select tv.tst_id2 from test_lookup tl where tl.tst_value = 'findMe' );
Run Code Online (Sandbox Code Playgroud)

它显式使用test_values列(通过tv别名);您的原始查询也在做同样的事情,但隐含地。

  • @evilive - 不是第一次,也可能不是最后一次......无论如何我都会把它留在这里,因为它*略有不同并且有一个文档链接*8-) (3认同)