【问题标题】:Query with broken sub-select should result in error but returns rows带有损坏的子选择的查询应该导致错误但返回行
【发布时间】:2016-04-08 04:32:48
【问题描述】:

我不理解这种情况下的行为。据我了解,带有无效子查询的查询应该会导致错误。但在这个例子中,它返回了一些行。

测试数据:

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;

按预期工作:

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

但以下查询也在检索行,显然是通过从“test_values”表中获取“test_id2”列,而不是从子查询中所述的“test_lookup”表中获取,尽管没有使用别名内部和外部。

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         
*/

【问题讨论】:

  • @vmachan OP 发布了一个完整的测试用例,包括创建表和插入语句。我不确定您希望 OP 为他们的问题添加哪些额外信息!
  • 我在这里stackoverflow.com/questions/34774242/…问了一个类似的问题。如果您要问如何改变这种行为……看来您不能。
  • 是的。它使用正确(但更长)的代码,一切都很好;)

标签: sql oracle subquery correlated-subquery


【解决方案1】:

原因是当子查询中不存在非别名列但外部查询中存在时,Oracle 假定您引用的是外部查询中的列。

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

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

希望这能让事情更清楚吗?

您看到的问题是一个很好的例子,说明了为什么您应该始终使用它们来自哪个表来标记您的列 - 这使得维护查询开始变得更加容易!

【讨论】:

  • 谢谢。事实上,我仍然对它的工作原理感到惊讶。考虑到不是选择,而是以某种方式存储的删除语句,您可能会通过更改表“B”的列而无意中从表“A”中删除行,甚至不触及语句本身urghs。好的,现在在使用多个表时使用别名;)
  • @evilive 由于相关的子查询,它的工作原理是这样的——例如select * from table1 t1 where exists (select null from table2 t2 where t1.col1 = t2.col1);。 IE。外部查询的范围扩展到其中任何子查询的顶层。因此,如果查询明确指出它正在使用哪些列,Oracle 必须猜测该列来自哪个表。以及用于发现另一个案例以支持使用别名的道具!
  • @evilive:这实际上并不特定于 Oracle。这就是在 SQL 标准中定义名称解析的方式
【解决方案2】:

当您的“损坏”查询用作子查询时,它仍然可以引用外部查询的表列;这是相关工作所必需的。它从test_values 表中提取tst_id2 列。如果两个表具有相同的列,则内部表将优先,但这里不是这种情况。

From the documentation:

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

但这有效:

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

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

【讨论】:

  • 谢谢,Boneist 快了一点
  • @evilive - 不是第一次,也可能不是最后一次...无论如何我都会把它留在这里,因为它略有不同并且有一个文档链接* 8-)
  • @AlexPoole 为文档链接万岁! *{;-D(附:新年快乐!)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-27
  • 1970-01-01
  • 2019-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多