检查以下示例(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标准中没有定义的吗?
答案 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"
列。我们知道表t1
和t2
都有相同的列名,但是给了本地列的首选项。
第二个SQL。
select * from t1 where id in (select id from t3 where a = 'aa');
执行第二个查询时,它会从表格"a"
中选择列t1
。
因为没有任何列名为" a"因此SQL将使用不同的属性来执行查询。