自然加入自然加入

时间:2018-05-24 17:23:23

标签: sql oracle

之间有什么区别
select * from degreeprogram  NATURAL JOIN degreeprogram ;

select * from degreeprogram d1 NATURAL JOIN degreeprogram d2;
在oracle中? 我希望他们返回相同的结果集,但是,他们没有。第二个查询执行我所期望的:它使用相同的命名属性连接两个关系,因此它返回与存在于degreeprogram中相同的元组。但是,第一个查询对我来说很困惑:这里,每个元组在结果集中出现多次 - >这里使用了什么连接条件?

谢谢

4 个答案:

答案 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指示数据库

  1. 查找两个表共有的所有列名(在本例中为degreeprogramdegreeprogram,当然这些列具有相同的列。)
  2. table1.column1 = table2.column1的形式为每对匹配的列名生成一个连接条件(在这种情况下,degreeprogram中的每一列都会有一个。)
  3. 因此像这样的查询

    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;
    

    然后tracing the session

    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允许这个传递,但是这样做它应该做一些事情来保持表实例分开(例如在内部为它们创建别名)。它显然不会将结果与表中的行数相乘。一个错误。