带有不明确列的SQL

时间:2016-10-05 10:41:36

标签: sql oracle

检查以下示例(Oracle语法):

create table t1 (id number, a varchar2(255));
create table t2 (id number, a varchar2(255));
create table t3 (id number);

select * from t1 where id in (select id from t2 where a = 'aa');
select * from t1 where id in (select id from t3 where a = 'aa');

两者都选择正常工作,但他们使用不同的属性进行过滤。在我看来,首先SQL应该给出错误,因为列a是模糊定义的。这是在官方SQL标准中没有定义的吗?

3 个答案:

答案 0 :(得分:5)

这不是最清晰的陈述,但是the documentation does refer to this behaviour

  

如果子查询中的列与contains语句中的列具有相同的名称,则必须使用包含表名或别名的contains语句对表的列的任何引用作为前缀。为了使您的语句更易于阅读,请始终使用表,视图或实例化视图的名称或别名限定子查询中的列。

所以基本上说,对于你的第一个引用t1.a的陈述,你必须明确地使用它的名字(或别名):

select * from t1 where id in (select id from t2 where t1.a = 'aa');

如果你不这样做,它默认会使用子查询表。它对于解析器来说并不含糊,因为它将首先查看相同级别(子)查询中的表,并且只有在它无法在当前级别找到该列时才查看外层。这可能不是你所期待的那样,我同意抱怨它是模棱两可的,以避免产生错误结果的细微错误;但它是如何运作的。

但正如它所说的那样,无论如何总是明白更安全:

select * from t1 where t1.id in (select t2.id from t2 where t2.a = 'aa');

select * from t1 where t1.id in (select t2.id from t2 where t1.a = 'aa');

和你的第二个陈述:

select * from t1 where t1.id in (select t3.id from t3 where t1.a = 'aa');

答案 1 :(得分:1)

基于长期的编程原则,行为正是应该的样子。我还没有看到SQL标准(显然它不能免费检查),但在编程中一般来说"碰撞"名称之间是最常见的问题之一,至少在某些情况下有非常简单的规则。这是其中之一:本地名称将始终掩盖调用环境中可能存在的相同名称。

令人遗憾的是,Oracle本身并不总是遵循这个简单的规则。在事实上的子查询中存在一些与此相关的错误(在OTN上已经对此进行了一些讨论,导致向Oracle提交错误报告)。

答案 2 :(得分:-1)

第一个SQL。

select * from t1 where id in (select id from t2 where a = 'aa');

第一个查询将在执行查询时使用t2表的"a"列。我们知道表t1t2都有相同的列名,但是给了本地列的首选项。

第二个SQL。

select * from t1 where id in (select id from t3 where a = 'aa');

执行第二个查询时,它会从表格"a"中选择列t1

因为没有任何列名为" a"因此SQL将使用不同的属性来执行查询。