oracle 在查询中两次使用同一个表别名

siotufzp  于 2023-03-29  发布在  Oracle
关注(0)|答案(2)|浏览(203)

我的同事是ANSI连接语法的新手,他最近写了一个这样的查询:

SELECT count(*)
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b)
       JOIN table3 t3 ON
             (t3.col_c = t1.col_c);

请注意,table3在不同的列上联接到table1和table2,但这两个JOIN子句对table3使用相同的表别名。
查询运行,但我不确定它的有效性。这是编写此查询的有效方法吗?
我认为连接应该是这样的:

SELECT count(*)
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b AND
             t3.col_c = t1.col_c);

**这两个版本的功能是否相同?**我的数据库中没有足够的数据来确定。

谢谢。

5f0d552i

5f0d552i1#

第一个查询是4个表的连接,第二个查询是3个表的连接,所以我不希望两个查询返回相同的行数。

SELECT *
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b)
       JOIN table3 t3 ON
             (t3.col_c = t1.col_c);

别名t3只在ON子句中使用。别名t3引用ON关键字之前的表。我通过实验发现了这一点。因此前面的查询相当于

SELECT *
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b)
       JOIN table3 t4 ON
             (t4.col_c = t1.col_c);

这可以在传统的连接中转换

SELECT *
  FROM table1 t1,
       table2 t2,
       table3 t3,
       table3 t4
where (t1.col_a = t2.col_a)
    and  (t2.col_b = t3.col_b)
    and (t4.col_c = t1.col_c);

第二个查询是

SELECT *
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b AND
             t3.col_c = t1.col_c);

这也可以在传统的连接中转换

SELECT *
  FROM table1 t1,
    table2 t2,
    table3 t3
where (t1.col_a = t2.col_a)
    and (t2.col_b = t3.col_b)
    AND (t3.col_c = t1.col_c);

这些查询似乎是不同的。为了证明它们的差异,我们使用以下示例:

create table table1(
    col_a number,
    col_c number
);

create table table2(
    col_a number,
    col_b number
);

create table table3(
    col_b number,
    col_c number
);

insert into table1(col_a, col_c) values(1,3);
insert into table1(col_a, col_c) values(4,3);
insert into table2(col_a, col_b) values(1,2);
insert into table2(col_a, col_b) values(4,2);
insert into table3(col_b, col_c) values(2,3);
insert into table3(col_b, col_c) values(2,5);
insert into table3(col_b, col_c) values(7,9);

commit;

我们得到以下输出

SELECT *
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b)
       JOIN table3 t3 ON
             (t3.col_c = t1.col_c)

| COL_A | COL_C | COL_A | COL_B | COL_B | COL_C | COL_B | COL_C |
|-------|-------|-------|-------|-------|-------|-------|-------|
|     1 |     3 |     1 |     2 |     2 |     3 |     2 |     3 |
|     4 |     3 |     4 |     2 |     2 |     3 |     2 |     3 |
|     1 |     3 |     1 |     2 |     2 |     5 |     2 |     3 |
|     4 |     3 |     4 |     2 |     2 |     5 |     2 |     3 |


             
SELECT *
  FROM table1 t1
       JOIN table2 t2 ON
            (t1.col_a = t2.col_a)
       JOIN table3 t3 ON
            (t2.col_b = t3.col_b AND
             t3.col_c = t1.col_c)

| COL_A | COL_C | COL_A | COL_B | COL_B | COL_C |
|-------|-------|-------|-------|-------|-------|
|     4 |     3 |     4 |     2 |     2 |     3 |
|     1 |     3 |     1 |     2 |     2 |     3 |

检索的行数不同,因此count(*)也不同。
使用化名是令人惊讶的,至少对我来说。
下面的查询可以工作,因为where_clause中的t1引用了table2

select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_b<0;

下面的查询可以工作,因为where_clause中的t1引用了table1

select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_c<0;

以下查询引发错误,因为table1table2都包含列col_a

select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_a<0;

抛出的错误是

ORA-00918: column ambiguously defined

下面的查询有效,别名t1引用同一个where_clause中的两个不同的表。

select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_b<0 and t1.col_c<0;

这些和更多的例子可以在这里找到:http://sqlfiddle.com/#!4/84feb/12

最小计数器示例

最小的反例是

table1
col_a  col_c
    1      2

table2
col_a  col_b
    1      3

table3
col_b  col_c
    3      5
    6      2

这里,第二个查询的结果集为空,第一个查询返回一行。可以证明,第二个查询的count(*)永远不会超过第一个查询的count(*)

更详细的说明

如果我们详细分析下面的陈述,这种行为将变得更加清楚。

SELECT t.col_b, t.col_c
  FROM table1 t
       JOIN table2 t ON
            (t.col_b = t.col_c) ;

以下是此查询的简化语法,采用Backus-Naur形式,源自Oracle 12.2的SQL Language Reference中的语法描述。请注意,在每个语法图下都有一个指向此图的 Backus-Naur 形式的链接,例如Description of the illustration select.eps。“简化”意味着我忽略了所有未使用的可能性,例如select定义为

select::=subquery [ for_update_clause ] ;

我们的查询不使用可选的for_update_clause,因此我将规则简化为

select::=subquery

唯一的例外是可选的where-clause。我没有删除它,以便即使我们添加where_clause,也可以使用此简化规则来分析上述查询。
这些简化规则将只定义所有可能的选择语句的子集。

select::=subquery 
subquery::=query_block
query_block::=SELECT select_list FROM join_clause [ where_clause ]
join_clause::=table_reference  inner_cross_join_clause ...  
table_reference::=query_table_expression  t_alias query_table_expression::=table
inner_cross_join_clause::=JOIN table_reference ON condition

因此,我们的select语句是query_blockjoin_clause的类型为

table_reference inner_cross_join_clause

其中table_referencetable1 tinner_cross_join_clauseJOIN table2 t ON (t.col_b = t.col_c)。省略号...意味着可能有额外的inner_cross_join_clauses,但我们这里不需要。
inner_cross_join_clause中,别名t引用了table2。只有当这些引用不能满足时,别名才必须在外部作用域中搜索。因此ON条件中的所有以下表达式都有效:

t.col_b = t.col_c

这里t.col_btable2.col_b,因为t引用了它的inner_cross_join_clause的别名,t.col_ctable1.col_cinner_cross_join_clauset(引用table2)没有列col_c,因此将搜索外部范围并找到适当的别名。
如果我们有条款

t.col_a = t.col_a

别名可以被发现为在该ON-condition所属的inner_cross_join_clause中定义的别名,因此t将被解析为table2
如果选择列表包括

t.col_c, t.col_b, t.col_a

而不是*,则join_clause将被搜索别名,t.col_c将被解析为table1.col_ctable2不包含列col_c),t.col_b将被解析为table2.col_btable1不包含col_b),但t.col_a将引发错误

ORA-00918: column ambiguously defined

因为对于select_list,没有一个aias定义优先于另一个。如果我们的查询也有一个where_clause,那么别名的解析方式就像在select_list中使用一样。

mitkmikd

mitkmikd2#

数据越多,结果就越不一样,你同事的查询和这个一样。

select * from table3 where t3.col_b = 'XX'
union
select * from table3 where t3.col_c = 'YY'

select * from table3 where t3.col_b = 'XX' or t3.col_c = 'YY'

而你的查询是这样的。

select * from table3 where t3.col_b ='XX' and t3.col_c='YY'

第一个是data where(xx or yy),第二个是data where(xx and yy)。

相关问题