之间有什么区别
select * from degreeprogram NATURAL JOIN degreeprogram ;
和
select * from degreeprogram d1 NATURAL JOIN degreeprogram d2;
在oracle中?
我希望他们返回相同的结果集,但是,他们没有。第二个查询执行我所期望的:它使用相同的命名属性连接两个关系,因此它返回与存在于degreeprogram中相同的元组。但是,第一个查询对我来说很困惑:这里,每个元组在结果集中出现多次 - >这里使用了什么连接条件?
谢谢
答案 0 :(得分:1)
NATURAL JOIN
表示基于两个表中具有相同名称的所有列连接两个表。
我想,对于表中的每一列,Oracle在内部编写了如下条件:
degreeprogram.column1 = degreeprogram.column1
(由于ORA-00918
列模糊定义错误,您无法自行编写)
然后,我想,Oracle正在优化它只是
degreeprogram.column1 is not null
所以,你并没有完全得到你自己的CROSS JOIN
个表 - 只有CROSS JOIN
个没有空列的行。
更新:由于这是选定的答案,我将从Thorsten Kettner的答案中补充说,这种行为可能是甲骨文的一个错误。在18c中,当您尝试ORA-00918
表自身时,Oracle行为正常并返回NATURAL JOIN
错误。
答案 1 :(得分:0)
这两个语句之间的区别在于第二个语句明确定义了表上的自连接,第一个语句是优化器试图找出你真正想要的东西。在我的数据库中,第一个语句执行笛卡尔合并连接并且根本没有优化,第二个语句有一个更好的解释计划,使用索引扫描的单个全表访问。
答案 2 :(得分:0)
所谓的natural join
指示数据库
degreeprogram
和degreeprogram
,当然这些列具有相同的列。)table1.column1 = table2.column1
的形式为每对匹配的列名生成一个连接条件(在这种情况下,degreeprogram
中的每一列都会有一个。)因此像这样的查询
select count(*) from demo natural join demo;
将转变为
select count(*) from demo, demo where demo.x = demo.x;
我通过创建一个包含一列和两行的表来检查这一点:
create table demo (x integer);
insert into demo values (1);
insert into demo values (2);
commit;
SQL> alter session set tracefile_identifier='demo_trace';
Session altered.
SQL> alter session set events 'trace [SQL_Compiler.*]';
Session altered.
SQL> select /* nj test */ count(*) from demo natural join demo;
COUNT(*)
----------
4
1 row selected.
SQL> alter session set events 'trace [SQL_Compiler.*] off';
Session altered.
然后在12_ora_6196_demo_trace.trc中找到了这一行:
Final query after transformations:******* UNPARSED QUERY IS *******
SELECT COUNT(*) "COUNT(*)" FROM "WILLIAM"."DEMO" "DEMO","WILLIAM"."DEMO" "DEMO" WHERE "DEMO"."X"="DEMO"."X"
以后几行:
try to generate single-table filter predicates from ORs for query block SEL$58A6D7F6 (#0)
finally: "DEMO"."X" IS NOT NULL
(这仅仅是上面生成的查询之上的优化,因为列X
可以为空,但是连接允许优化器推断只需要非空值。它不会替换连接。)
因此执行计划:
-----------------------------------------+-----------------------------------+
| Id | Operation | Name | Rows | Bytes | Cost | Time |
-----------------------------------------+-----------------------------------+
| 0 | SELECT STATEMENT | | | | 7 | |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | MERGE JOIN CARTESIAN | | 4 | 52 | 7 | 00:00:01 |
| 3 | TABLE ACCESS FULL | DEMO | 2 | 26 | 3 | 00:00:01 |
| 4 | BUFFER SORT | | 2 | | 4 | 00:00:01 |
| 5 | TABLE ACCESS FULL | DEMO | 2 | | 2 | 00:00:01 |
-----------------------------------------+-----------------------------------+
Query Block Name / Object Alias(identified by operation id):
------------------------------------------------------------
1 - SEL$58A6D7F6
3 - SEL$58A6D7F6 / DEMO_0001@SEL$1
5 - SEL$58A6D7F6 / DEMO_0002@SEL$1
------------------------------------------------------------
Predicate Information:
----------------------
3 - filter("DEMO"."X" IS NOT NULL)
或者,让我们看看dbms_utility.expand_sql_text
对它的作用。鉴于上面的跟踪文件,我不太清楚该怎么做,但它显示了类似的扩展:
SQL> var result varchar2(1000)
SQL> exec dbms_utility.expand_sql_text('select count(*) from demo natural join demo', :result)
PL/SQL procedure successfully completed.
RESULT
----------------------------------------------------------------------------------------------------------------------------------
SELECT COUNT(*) "COUNT(*)" FROM (SELECT "A2"."X" "X" FROM "WILLIAM"."DEMO" "A3","WILLIAM"."DEMO" "A2" WHERE "A2"."X"="A2"."X") "A1"
课程: NATURAL JOIN
是邪恶的。大家都知道这一点。
答案 3 :(得分:0)
我称这是一个错误。这个查询:
select * from degreeprogram d1 NATURAL JOIN degreeprogram d2;
转换为
select col1, col2, ... -- all columns
from degreeprogram d1
join degreeprogram d2 using (col1, col2, ...)
并为您提供表中所有列都不为空的所有行(因为using(col)
从不匹配空值)。
但是这个查询:
select * from degreeprogram NATURAL JOIN degreeprogram;
根据标准SQL,无效,因为每个表在查询中必须具有唯一的名称或别名。 Oracle允许这个传递,但是这样做它应该做一些事情来保持表实例分开(例如在内部为它们创建别名)。它显然不会将结果与表中的行数相乘。一个错误。