我的同事是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);
这两个版本功能完全相同吗?我的数据库中确实没有足够的数据可供使用。
感谢。
答案 0 :(得分:4)
第一个查询是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(*)
不同。
别名的使用令人惊讶。至少对我而言。
以下查询有效,因为t1
引用where_clause
中的table2
。
select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_b<0;
以下查询有效,因为t1
引用where_clause
中的table1
。
select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_c<0;
以下查询引发错误,因为table1
和table2
都包含列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语句的子集。
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_block
,join_clause
的类型为
table_reference inner_cross_join_clause
其中table_reference
为table1 t
,inner_cross_join_clause
为JOIN table2 t ON (t.col_b = t.col_c)
。省略号...
表示可能有额外的inner_cross_join_clauses,但我们不需要这个。
inner_cross_join_clause
中的别名t
引用table2
。只有在不能满足这些引用的情况下,才能在外部范围内搜索别名。因此ONcondition中的所有以下表达式都是有效的:
t.col_b = t.col_c
此处t.col_b
为table2.col_b
,因为t
引用其inner_cross_join_clause
的别名,t.col_c
为table1.col_c
。 t
的{{1}}(参考inner_cross_join_clause
)没有列table2
,因此将搜索外部范围并找到相应的别名。
如果我们有条款
col_c
别名可以在此{ - 1}}所定义的t.col_a = t.col_a
中定义的别名中找到,因此inner_cross_join_clause
将被解析为condition
。
如果选择列表包含
t
而不是table2
,我们会在t.col_c, t.col_b, t.col_a
搜索别名,*
将被解析为join_clause
(t.col_c
不包含列{ {1}}),table1.col_c
将被解析为table2
(col_c
不包含t.col_b
),但table2.col_b
会引发错误
table1
因为对于select_list,所有aias定义都没有先于另一个定义。如果我们的查询也有col_b
,那么别名的解析方式与在t.col_a
中使用的方式相同。
答案 1 :(得分:3)
有了更多数据,它会产生不同的结果。 您的同事查询与此相同。
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'
第一个是数据,其中(xx或yy),而第二个是数据,其中(xx和yy)