如何在Oracle中更改JOIN顺序?

问题:

您的查询在多个表上具有JOIN条件时会遇到性能问题,并且Oracle优化器未选择所需的联接顺序。

解:

Oracle有两个提示,即ORDERED提示和LEADING提示,可用于影响查询中使用的联接顺序。

订购提示

您要运行SQL来联接两个表,student和Department,因为您要获取每个学生的部门名称。通过在查询中放置一个ORDERED提示,您可以看到该提示如何更改执行访问路径。

示例

EXPLAIN PLAN FOR
SELECT first_name, department_name
  FROM students e, departments d
WHERE e.department_id = d.department_id;
 
SELECT *
FROM table(DBMS_XPLAN.DISPLAY (FORMAT=>'BASIC'));

输出结果

----------------------------------------------------------
| Id  | Operation                    | Name              |
----------------------------------------------------------
|   0 | SELECT STATEMENT             |                   |
|   1 |  MERGE JOIN                  |                   |
|   2 |   TABLE ACCESS BY INDEX ROWID| DEPARTMENTS       |
|   3 |    INDEX FULL SCAN           | DEPT_ID_PK        |
|   4 |   SORT JOIN                  |                   |
|   5 |    VIEW                      | index$_join$_001  |
|   6 |     HASH JOIN                |                   |
|   7 |      INDEX FAST FULL SCAN    | stu_DEPARTMENT_IX |
|   8 |      INDEX FAST FULL SCAN    | stu_NAME_IX       |
----------------------------------------------------------

示例

EXPLAIN PLAN FOR
SELECT /*+ ordered */ first_name, department_name
  FROM students e, departments d
WHERE e.department_id = d.department_id;

输出结果

-----------------------------------------------------
| Id  | Operation               | Name              |
-----------------------------------------------------
|   0 | SELECT STATEMENT        |                   |
|   1 |  HASH JOIN              |                   |
|   2 |   VIEW                  | index$_join$_001  |
|   3 |    HASH JOIN            |                   |
|   4 |     INDEX FAST FULL SCAN| stu_DEPARTMENT_IX |
|   5 |     INDEX FAST FULL SCAN| stu_NAME_IX       |
|   6 |   TABLE ACCESS FULL     | DEPARTMENTS       |
-----------------------------------------------------

领先的提示

与使用ORDERED提示的示例一样,您具有相同的控件来指定查询的连接顺序。与LEADING提示的区别在于,您可以从提示本身内部指定连接顺序,而对于ORDERED提示,则在查询的FROM子句中指定连接顺序

示例

EXPLAIN PLAN FOR
SELECT /*+ leading(departments, students) */  first_name, department_name
  FROM students e, departments d
WHERE e.department_id = d.department_id;
 
 
SELECT *
FROM table(DBMS_XPLAN.DISPLAY (FORMAT=>'BASIC'));

输出结果

----------------------------------------------------------
| Id  | Operation                    | Name              |
----------------------------------------------------------
|   0 | SELECT STATEMENT             |                   |
|   1 |  MERGE JOIN                  |                   |
|   2 |   TABLE ACCESS BY INDEX ROWID| DEPARTMENTS       |
|   3 |    INDEX FULL SCAN           | DEPT_ID_PK        |
|   4 |   SORT JOIN                  |                   |
|   5 |    VIEW                      | index$_join$_001  |
|   6 |     HASH JOIN                |                   |
|   7 |      INDEX FAST FULL SCAN    | stu_DEPARTMENT_IX |

提示将为优化程序节省确定最佳连接顺序时必须处理所有可能的连接顺序的时间。提示可以提高查询性能,尤其是随着查询中要联接的表数量的增加。

使用这些提示中的任何一个时,您都可以指导优化器有关表的连接顺序。因此,至关重要的是要知道提示将提高查询的性能。Oracle建议在可能的情况下建议在ORDERED提示上使用LEADING提示,因为LEADING提示具有更多的内置功能。指定ORDERED提示时,可以从FROM子句的表列表中指定连接顺序,同时使用LEADING提示,您可以在提示本身内指定连接顺序。

数据准备:用于该问题的数据如下所示。数据完全用于演示目的。

DROP TABLE students;
COMMIT;

CREATE TABLE students
    ( student_id     NUMBER(6)
    , first_name     VARCHAR2(20)
    , last_name      VARCHAR2(25) 
    , email          VARCHAR2(40) 
    , phone_number   VARCHAR2(20)
    , join_date      DATE 
    , class_id       VARCHAR2(20)  
    , fees           NUMBER(8,2)
    , professor_id   NUMBER(6)
    , department_id  NUMBER(4) 
    ) ;

示例

CREATE UNIQUE INDEX stu_id_pk ON students (student_id) ;
INSERT INTO students VALUES (100,'SMITH','JAMES','SMITH.JAMES@notreal.com','111.111.1245',TO_DATE('17-06-2003','DD-MM-YYYY'),'INS_CHAIRMAN',24000,NULL,NULL);
INSERT INTO students VALUES (101,'JOHNSON','JOHN','JOHNSON.JOHN@notreal.com','111.111.1246',TO_DATE('21-09-2005','DD-MM-YYYY'),'INS_VP',17000,100,90);
INSERT INTO students VALUES (102,'WILLIAMS','ROBERT','WILLIAMS.ROBERT@notreal.com','111.111.1247',TO_DATE('13-01-2001','DD-MM-YYYY'),'INS_VP',17000,100,90);
INSERT INTO students VALUES (103,'BROWN','MICHAEL','BROWN.MICHAEL@notreal.com','111.111.1248',TO_DATE('03-01-2006','DD-MM-YYYY'),'INS_STAFF',9000,102,60);
INSERT INTO students VALUES (104,'JONES','WILLIAM','JONES.WILLIAM@notreal.com','111.111.1249',TO_DATE('21-05-2007','DD-MM-YYYY'),'INS_STAFF',6000,103,60);
INSERT INTO students VALUES (105,'MILLER','DAVID','MILLER.DAVID@notreal.com','111.111.1250',TO_DATE('25-06-2005','DD-MM-YYYY'),'INS_STAFF',4800,103,60);
INSERT INTO students VALUES (106,'DAVIS','RICHARD','DAVIS.RICHARD@notreal.com','111.111.1251',TO_DATE('05-02-2006','DD-MM-YYYY'),'INS_STAFF',4800,103,60);
INSERT INTO students VALUES (107,'GARCIA','CHARLES','GARCIA.CHARLES@notreal.com','111.111.1252',TO_DATE('07-02-2007','DD-MM-YYYY'),'INS_STAFF',4200,103,60);
INSERT INTO students VALUES (108,'RODRIGUEZ','JOSEPH','RODRIGUEZ.JOSEPH@notreal.com','111.111.1253',TO_DATE('17-08-2002','DD-MM-YYYY'),'CL_PHY',12008,101,100);
INSERT INTO students VALUES (109,'WILSON','THOMAS','WILSON.THOMAS@notreal.com','111.111.1254',TO_DATE('16-08-2002','DD-MM-YYYY'),'CL_MATH',9000,108,100);
INSERT INTO students VALUES (110,'MARTINEZ','CHRISTOPHER','MARTINEZ.CHRISTOPHER@notreal.com','111.111.1255',TO_DATE('28-09-2005','DD-MM-YYYY'),'CL_MATH',8200,108,100);
INSERT INTO students VALUES (111,'ANDERSON','DANIEL','ANDERSON.DANIEL@notreal.com','111.111.1256',TO_DATE('30-09-2005','DD-MM-YYYY'),'CL_MATH',7700,108,100);
INSERT INTO students VALUES (112,'TAYLOR','PAUL','TAYLOR.PAUL@notreal.com','111.111.1257',TO_DATE('07-03-2006','DD-MM-YYYY'),'CL_MATH',7800,108,100);
INSERT INTO students VALUES (113,'THOMAS','MARK','THOMAS.MARK@notreal.com','111.111.1258',TO_DATE('07-12-2007','DD-MM-YYYY'),'CL_MATH',6900,108,100);

COMMIT;