在查询中两次使用相同的表别名

And*_*Dan 5 sql oracle ansi

我的同事是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);
Run Code Online (Sandbox Code Playgroud)

请注意,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);
Run Code Online (Sandbox Code Playgroud)

两个版本在功能上是否相同? 我不确定数据库中是否真的有足够的数据。

谢谢。

mir*_*173 5

第一个查询是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);
Run Code Online (Sandbox Code Playgroud)

别名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);
Run Code Online (Sandbox Code Playgroud)

可以在传统的连接中进行转换

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);
Run Code Online (Sandbox Code Playgroud)

第二个查询是

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);
Run Code Online (Sandbox Code Playgroud)

这也可以转变成传统的联接

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);
Run Code Online (Sandbox Code Playgroud)

这些查询似乎有所不同。为了证明它们的区别,我们使用以下示例:

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;
Run Code Online (Sandbox Code Playgroud)

我们得到以下输出

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 |
Run Code Online (Sandbox Code Playgroud)

检索的行数不同,因此count(*)也不同。

别名的使用令人惊讶。至少对于我来说。

以下查询有效,因为t1where_clause参考文献中table2

select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_b<0;
Run Code Online (Sandbox Code Playgroud)

以下查询有效,因为t1where_clause参考文献中table1

select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_c<0;
Run Code Online (Sandbox Code Playgroud)

下面的查询提出,因为这两个错误table1,并table2包含一列col_a

select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_a<0;
Run Code Online (Sandbox Code Playgroud)

引发的错误是

ORA-00918: column ambiguously defined
Run Code Online (Sandbox Code Playgroud)

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

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

这些和更多示例可以在这里找到: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    
Run Code Online (Sandbox Code Playgroud)

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

更详细的解释

如果我们详细分析以下语句,此行为将变得更加清楚。

SELECT t.col_b, t.col_c
  FROM table1 t
       JOIN table2 t ON
            (t.col_b = t.col_c) ;
Run Code Online (Sandbox Code Playgroud)

这是从Oracle 12.2 的SQL语言参考中的语法描述派生的Backus–Naur形式的此查询的简化语法。请注意,在每个语法图下都有一个指向该图的Backus–Naur形式的链接,例如,插图select.eps的描述。“减少”表示我遗漏了所有未使用的可能性,例如 在select被定义为

select::=subquery [ for_update_clause ] ;
Run Code Online (Sandbox Code Playgroud)

我们的查询没有使用optional for_update_clause,所以我将规则简化为

select::=subquery
Run Code Online (Sandbox Code Playgroud)

唯一的豁免是可选的where-clause。我没有删除它,因此即使我们添加了,该简化的规则也可以用于分析上述查询where_clause

这些简化的规则将仅定义所有可能的select语句的子集。

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
Run Code Online (Sandbox Code Playgroud)

所以我们的select语句是a query_blockjoin_clauseis类型

table_reference inner_cross_join_clause
Run Code Online (Sandbox Code Playgroud)

这里table_referencetable1 tinner_cross_join_clauseJOIN table2 t ON (t.col_b = t.col_c)。省略号...意味着可能会有另外的inner_cross_join_clauses,但是我们在这里不需要。

inner_cross_join_clause别名ttable2。仅当无法满足这些引用时,才必须在外部范围内搜索别名。因此,ON条件中的以下所有表达式均有效:

t.col_b = t.col_c
Run Code Online (Sandbox Code Playgroud)

t.col_btable2.col_b因为t指的是它的别名inner_cross_join_clauset.col_ctable1.col_ct所述的inner_cross_join_clause(指的table2)不具有柱col_c所以外部范围将被搜索和适当的别名会被发现。

如果我们有该条款

t.col_a = t.col_a
Run Code Online (Sandbox Code Playgroud)

该别名可以作为inner_cross_join_clause此ON- condition所属的别名定义,因此t将解析为table2

如果选择列表包含

t.col_c, t.col_b, t.col_a
Run Code Online (Sandbox Code Playgroud)

而不是*那么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
Run Code Online (Sandbox Code Playgroud)

因为对于select_list,任何aias定义都没有另一个定义。如果我们的查询中也包含,where_clause则别名的解析方式与在中使用别名的方式相同select_list