SQL> CREATE TABLE t1 (
2 id NUMBER NOT NULL,
3 n NUMBER,
4 pad VARCHAR2(4000),
5 CONSTRAINT t1_pk PRIMARY KEY(id)
6 );
Table created.
SQL> CREATE TABLE t2 (
2 id NUMBER NOT NULL,
3 t1_id NUMBER NOT NULL,
4 n NUMBER,
5 pad VARCHAR2(4000),
6 CONSTRAINT t2_pk PRIMARY KEY(id),
7 CONSTRAINT t2_t1_fk FOREIGN KEY (t1_id) REFERENCES t1
8 );
Table created.
SQL> CREATE TABLE t3 (
2 id NUMBER NOT NULL,
3 t2_id NUMBER NOT NULL,
4 n NUMBER,
5 pad VARCHAR2(4000),
6 CONSTRAINT t3_pk PRIMARY KEY(id),
7 CONSTRAINT t3_t2_fk FOREIGN KEY (t2_id) REFERENCES t2
8 );
Table created.
SQL> CREATE TABLE t4 (
2 id NUMBER NOT NULL,
3 t3_id NUMBER NOT NULL,
4 n NUMBER,
5 pad VARCHAR2(4000),
6 CONSTRAINT t4_pk PRIMARY KEY(id),
7 CONSTRAINT t4_t3_fk FOREIGN KEY (t3_id) REFERENCES t3
8 );
Table created.
SQL> execute dbms_random.seed(0)
PL/SQL procedure successfully completed.
SQL> INSERT INTO t1 SELECT rownum, rownum, dbms_random.string('a',50) FROM dual CONNECT BY level <= 10 ORDER BY dbms_random.random;
10 rows created.
SQL> INSERT INTO t2 SELECT 100+rownum, t1.id, 100+rownum, t1.pad FROM t1, t1 dummy ORDER BY dbms_random.random;
100 rows created.
SQL> INSERT INTO t3 SELECT 1000+rownum, t2.id, 1000+rownum, t2.pad FROM t2, t1 dummy ORDER BY dbms_random.random;
1000 rows created.
SQL> INSERT INTO t4 SELECT 10000+rownum, t3.id, 10000+rownum, t3.pad FROM t3, t1 dummy ORDER BY dbms_random.random;
10000 rows created.
SQL> COMMIT;
Commit complete.
使用 hint 讓執行計劃以 T3 作為驅動表
SQL> select /*+ leading(t3) use_merge(t4) */ *
2 from t3, t4
3 where t3.id = t4.t3_id and t3.n = 1100;
10 rows selected.
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------
SQL_ID g0rdyg9hdh9m0, child number 0
-------------------------------------
select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id =
t4.t3_id and t3.n = 1100
Plan hash value: 3831111046
-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.02 | 119 | | | |
| 1 | MERGE JOIN | | 1 | 10 | 10 |00:00:00.02 | 119 | | | |
| 2 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 15 | 2048 | 2048 | 2048 (0)|
|* 3 | TABLE ACCESS FULL| T3 | 1 | 1 | 1 |00:00:00.01 | 15 | | | |
|* 4 | SORT JOIN | | 1 | 10000 | 10 |00:00:00.02 | 104 | 974K| 535K| 865K (0)|
| 5 | TABLE ACCESS FULL| T4 | 1 | 10000 | 10000 |00:00:00.01 | 104 | | | |
-----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T3"."N"=1100)
4 - access("T3"."ID"="T4"."T3_ID")
filter("T3"."ID"="T4"."T3_ID")
使用 hint 讓執行計劃以 T4 作為驅動表
SQL> select /*+ leading(t4) use_merge(t3) */ *
2 from t3, t4
3 where t3.id = t4.t3_id and t3.n = 1100;
10 rows selected.
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------------------
SQL_ID gxuwn06y1c1az, child number 0
-------------------------------------
select /*+ leading(t4) use_merge(t3) */ * from t3, t4 where t3.id =
t4.t3_id and t3.n = 1100
Plan hash value: 875334572
-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 10 |00:00:00.04 | 119 | | | |
| 1 | MERGE JOIN | | 1 | 10 | 10 |00:00:00.04 | 119 | | | |
| 2 | SORT JOIN | | 1 | 10000 | 1001 |00:00:00.04 | 104 | 974K| 535K| 865K (0)|
| 3 | TABLE ACCESS FULL| T4 | 1 | 10000 | 10000 |00:00:00.01 | 104 | | | |
|* 4 | SORT JOIN | | 1001 | 1 | 10 |00:00:00.01 | 15 | 2048 | 2048 | 2048 (0)|
|* 5 | TABLE ACCESS FULL| T3 | 1 | 1 | 1 |00:00:00.01 | 15 | | | |
-----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("T3"."ID"="T4"."T3_ID")
filter("T3"."ID"="T4"."T3_ID")
5 - filter("T3"."N"=1100)
從返回的執行計劃結果中我們可以看到:2. 以 T3 為驅動表時, T3 訪問一次, T4 也是訪問一次; 以 T4 為驅動表時, T4 訪問一次, T3 也是訪問一次
3. 需要排序, 如果 PGA 空間重足時在 PGA 中排序, 不如果不足則交換到磁盤上排序
SQL> select /*+ leading(t3) use_merge(t4) */ *
2 from t3, t4
3 where t3.id = t4.t3_id and t3.n = 1100 and t4.n = 10034;
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------
SQL_ID bg9h60c7ak3ud, child number 0
-------------------------------------
select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id =
t4.t3_id and t3.n = 1100 and t4.n = 10034
Plan hash value: 3831111046
-----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 119 | | | |
| 1 | MERGE JOIN | | 1 | 1 | 1 |00:00:00.01 | 119 | | | |
| 2 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 15 | 2048 | 2048 | 2048 (0)|
|* 3 | TABLE ACCESS FULL| T3 | 1 | 1 | 1 |00:00:00.01 | 15 | | | |
|* 4 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 104 | 2048 | 2048 | 2048 (0)|
|* 5 | TABLE ACCESS FULL| T4 | 1 | 1 | 1 |00:00:00.01 | 104 | | | |
-----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T3"."N"=1100)
4 - access("T3"."ID"="T4"."T3_ID")
filter("T3"."ID"="T4"."T3_ID")
5 - filter("T4"."N"=10034)
SQL> create index t4_n on t4(n);
Index created.
SQL> select /*+ leading(t3) use_merge(t4) */ *
2 from t3, t4
3 where t3.id = t4.t3_id and t3.n = 1100 and t4.n = 10034;
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------------------------------------------------
SQL_ID bg9h60c7ak3ud, child number 0
-------------------------------------
select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id =
t4.t3_id and t3.n = 1100 and t4.n = 10034
Plan hash value: 1501658231
------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 18 | 1 | | | |
| 1 | MERGE JOIN | | 1 | 1 | 1 |00:00:00.01 | 18 | 1 | | | |
| 2 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 15 | 0 | 2048 | 2048 | 2048 (0)|
|* 3 | TABLE ACCESS FULL | T3 | 1 | 1 | 1 |00:00:00.01 | 15 | 0 | | | |
|* 4 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 3 | 1 | 2048 | 2048 | 2048 (0)|
| 5 | TABLE ACCESS BY INDEX ROWID| T4 | 1 | 1 | 1 |00:00:00.01 | 3 | 1 | | | |
|* 6 | INDEX RANGE SCAN | T4_N | 1 | 1 | 1 |00:00:00.01 | 2 | 1 | | | |
------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("T3"."N"=1100)
4 - access("T3"."ID"="T4"."T3_ID")
filter("T3"."ID"="T4"."T3_ID")
6 - access("T4"."N"=10034)
SQL> create index t3_n on t3(n);
Index created.
SQL> select /*+ leading(t3) use_merge(t4) */ *
2 from t3, t4
3 where t3.id = t4.t3_id and t3.n = 1100 and t4.n = 10034;
SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'));
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID bg9h60c7ak3ud, child number 0
-------------------------------------
select /*+ leading(t3) use_merge(t4) */ * from t3, t4 where t3.id =
t4.t3_id and t3.n = 1100 and t4.n = 10034
Plan hash value: 1827980052
------------------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | Reads | OMem | 1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | 1 |00:00:00.01 | 6 | 1 | | | |
| 1 | MERGE JOIN | | 1 | 1 | 1 |00:00:00.01 | 6 | 1 | | | |
| 2 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 3 | 1 | 2048 | 2048 | 2048 (0)|
| 3 | TABLE ACCESS BY INDEX ROWID| T3 | 1 | 1 | 1 |00:00:00.01 | 3 | 1 | | | |
|* 4 | INDEX RANGE SCAN | T3_N | 1 | 1 | 1 |00:00:00.01 | 2 | 1 | | | |
|* 5 | SORT JOIN | | 1 | 1 | 1 |00:00:00.01 | 3 | 0 | 2048 | 2048 | 2048 (0)|
| 6 | TABLE ACCESS BY INDEX ROWID| T4 | 1 | 1 | 1 |00:00:00.01 | 3 | 0 | | | |
|* 7 | INDEX RANGE SCAN | T4_N | 1 | 1 | 1 |00:00:00.01 | 2 | 0 | | | |
------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - access("T3"."N"=1100)
5 - access("T3"."ID"="T4"."T3_ID")
filter("T3"."ID"="T4"."T3_ID")
7 - access("T4"."N"=10034)
從上面的執行計劃中可以看出, 全表掃描後最後使用的 buffer 為 119, 在一個表上建立索引使用索引范圍掃描後 buffer 為 18, 在兩個表上建立的索引使用索引范圍掃描後 buffer 為 6. 由此可以見, 在表的謂詞條件上如果有索引的話, 將會提高執行效率.此外, 由於 sort merge joins 需要先在 PGA 中進行排序,, 如果 PGA 空間不足, 就會將數據交換到磁盤上進行排序。由於, 磁盤相對於內存來說是慢速設備,因此在磁盤上排序會比在內存上排序慢, 另外排序排序消耗的時間還需要加上數據在內存和磁盤上傳輸的時間,因此盡可能減少磁盤排序的次數也就會提高執行效率, 有兩種方法會減少磁盤排序:
1. 增大 PGA 的大小, 如果是 oracle 10g,需要增加參數 pga_aggregate_target 的大小,如果是 oracle 11g,則增加 memory_target 的大小
2. 減少排序的數據量, 一些不需要的字段就不要寫在 select 後面
四. 小結
遇到 sql 調優時,如果執行計劃顯示表的連接方式是 sort merge join:
首先,看看 sql 語句是不是表的連接方式有沒有可能轉換為 hash join(等值連接條件)
其次,只能使用 sort merge join 時看看表的謂詞條件上是不是有索引
最後,看看執行計劃排序占用的內存大小是不是在磁盤上有排序, 是不是能夠避免在磁盤上排序