1. 程式人生 > >Oracle 有表連線的connect by 的優化

Oracle 有表連線的connect by 的優化

說明

有1個有表連線,還有connect by 的SQL,整了好幾天才優化成功,感覺像遊戲中打死了只大boss一樣。現將這個過程整理一下。

優化前:

SELECT r.OUT_VER_BEGIN_IDdataID

FROM DMS_DATA_RELA r, DMS_OBJ o

WHERE r.DELETE_FLAG = '0'

AND r.RELA_TYPE_CODE = 'parent'

AND r.OUT_OBJ_CODE = o.OBJ_CODE

AND o.DELETE_FLAG = '0'

AND o.OPEN_STATE = '1'

AND r.IN_OBJ_CODE != 'o_in'

START WITH r.IN_DATA_ID in

(SELECT d.OUT_DATA_ID

FROM DMS_DATA_RELA d

where d.OUT_VER_BEGIN_ID = :1

and d.last_curent_flag = '1')

CONNECT BY r.IN_VER_BEGIN_ID = PRIOR r.OUT_VER_BEGIN_ID

優化後:

我把這個SQL先做了connect by 迴圈,然後再與另1個表做了連線,效果超好,我從李華值 《海量資料庫解決方案》3.2.5 找到相關例子,並有這樣的說明 : ”如果查詢條件中的列位於同一表中時,並沒有必要優先執行表連線“

select dataID

from(SELECT r.OUT_VER_BEGIN_ID dataID, r.out_obj_code

FROM DMS_DATA_RELA r

WHERE r.DELETE_FLAG = '0'

AND r.RELA_TYPE_CODE = 'parent'

AND r.IN_OBJ_CODE != 'o_in'

STARTWITH r.IN_DATA_ID in

(SELECT d.OUT_DATA_ID

FROM DMS_DATA_RELA d

where d.OUT_VER_BEGIN_ID=:1

and d.last_curent_flag= '1')

CONNECT BY r.IN_VER_BEGIN_ID = PRIOR r.OUT_VER_BEGIN_ID) e,

DMS_OBJ o

wheree.OUT_OBJ_CODE = o.OBJ_CODE

ANDo.DELETE_FLAG = '0'

ANDo.OPEN_STATE = '1'

優化過程:

曾中途一籌莫展時,到劉大的論壇求助過,下面是地址。

http://t.askmaclean.com/thread-3381-1-1.html

下面過程是基於以上的整理。有基本資訊和試過的方法

基本資訊

基本環境
作業系統:windows server 2008 r2 enterprise

SQL> select * from V$version;
BANNER
----------------------------------------------------------------
Oracle Database 10g Enterprise EditionRelease 10.2.0.4.0 - 64bi
PL/SQL Release 10.2.0.4.0 - Production
CORE 10.2.0.4.0 Production
TNS for 64-bit Windows: Version 10.2.0.4.0- Production
NLSRTL Version 10.2.0.4.0 - Production



開發中的庫,在做測試時,發現有1SQL比較消耗資源,我想把他優化一下。

SQL如下:
SELECT r.OUT_VER_BEGIN_ID dataID
FROM DMS_DATA_RELA r, DMS_OBJ o
WHERE r.DELETE_FLAG = '0'
AND r.RELA_TYPE_CODE ='parent'
AND r.OUT_OBJ_CODE =o.OBJ_CODE
AND o.DELETE_FLAG = '0'
AND o.OPEN_STATE = '1'
AND r.IN_OBJ_CODE != 'o_in'
START WITH r.IN_DATA_ID in
(SELECT d.OUT_DATA_ID
FROM DMS_DATA_RELA d
where d.OUT_VER_BEGIN_ID = :1
and d.last_curent_flag = '1')
CONNECT BY r.IN_VER_BEGIN_ID = PRIORr.OUT_VER_BEGIN_ID

執行計劃: (來自awrsqrpt)

0 SELECT STATEMENT 8045 (100) 
1 FILTER 
2 CONNECT BY WITHFILTERING 
3 FILTER 
4 COUNT 
5 HASH JOIN 717K 111M 8045 (1)00:01:37 
6 TABLE ACCESSFULL DMS_OBJ 41 656 3 (0)00:00:01 
7 TABLE ACCESSFULL DMS_DATA_RELA 717K 100M 8037 (1)00:01:37 
8 TABLE ACCESS BY INDEX ROWIDDMS_DATA_RELA 1 91 4 (0) 00:00:01 
9 INDEX RANGE SCAN OUT_VER_BEGIN_ID_INDEX 1 3 (0) 00:00:01 
10 HASH JOIN 
11 CONNECT BY PUMP 
12 COUNT 
13 HASH JOIN 717K 111M 8045 (1)00:01:37 
14 TABLE ACCESSFULL DMS_OBJ 41 656 3 (0)00:00:01 
15 TABLE ACCESSFULL DMS_DATA_RELA 717K 100M 8037 (1)00:01:37 

隨便找個變數單獨執行這條SQL試了下,大概能執行6秒。

表上記錄數
SQL> select count(*) from dms_data_rela;
COUNT(*)
----------
858470

SQL> select count(*) from dms_obj;
COUNT(*)
----------
41


表上索引:(索引較多,如果不影響此條SQL的查詢效能,先不考慮這些索引合不合理,因為有些非技術因素)

SQL> selectindex_name,column_name,table_name from dba_ind_columns where table_name='DMS_DATA_RELA' order by index_name;
INDEX_NAME COLUMN_NAME TABLE_NAME
-------------------------------------------------- ------------------------------
CREATE_TIME_INDEX SYS_NC00031$ DMS_DATA_RELA
DELETE_FLAG_INDEX DELETE_FLAG DMS_DATA_RELA
IN_DATA_ID_INDEX IN_DATA_ID DMS_DATA_RELA
IN_DATA_NAME_INDEX IN_DATA_NAME DMS_DATA_RELA
IN_DATA_SOURCE_CODE_INDEX IN_DATA_SOURCE_CODE DMS_DATA_RELA
IN_DATA_SOURCE_ID_INDEX IN_DATA_SOURCE_ID DMS_DATA_RELA
IN_OBJ_CODE_INDEX IN_OBJ_CODE DMS_DATA_RELA
IN_VER_BEGIN_ID_INDEX IN_VER_BEGIN_ID DMS_DATA_RELA
LAST_CURENT_FLAG_INDEX LAST_CURENT_FLAG DMS_DATA_RELA
OUT_DATA_ID_INDEX OUT_DATA_ID DMS_DATA_RELA
OUT_DATA_NAME_INDEX OUT_DATA_NAME DMS_DATA_RELA
OUT_DATA_SOURCE_ID_INDEX OUT_DATA_SOURCE_ID DMS_DATA_RELA
OUT_OBJ_CODE_INDEX OUT_OBJ_CODE DMS_DATA_RELA
OUT_VER_BEGIN_ID_INDEX OUT_VER_BEGIN_ID DMS_DATA_RELA
PK_DMS_DATA_RELA RELA_ID DMS_DATA_RELA
RELA_TYPE_CODE_INDEX RELA_TYPE_CODE DMS_DATA_RELA



SQL> selectindex_name,column_name,table_name from dba_ind_columns where table_name='DMS_OBJ' order by index_name;
INDEX_NAME COLUMN_NAME TABLE_NAME
-------------------------------------------------- ------------------------------
PK_DMS_OBJ OBJ_ID DMS_OBJ

上面SQL走全表,我找了一些資料說是在IN_VER_BEGIN_ID 列上有索���的話,會走索引,但是實際有索引,還是全表,加hint也還是全表。
另外表也收集過統計資訊了。

表上的列:

SQL> desc DMS_DATA_RELA
Name Type Nullable Default Comments 
------------------------------ ------------- -------- ------- -------- 
RELA_ID CHAR(32) 
IN_DATA_ID VARCHAR2(200) Y 
IN_DATA_NAME VARCHAR2(200) Y 
IN_DATA_SOURCE_ID VARCHAR2(200) Y 
IN_DATA_SOURCE_CODE VARCHAR2(200) Y 
IN_OBJ_CODE VARCHAR2(200) Y 
IN_VER_BEGIN_ID VARCHAR2(200) Y 
IN_VER_BEGIN_DATA_SOURCE_ID VARCHAR2(200) Y 
IN_VER_BEGIN_DATA_SOURCE_CODE VARCHAR2(64) Y 
IN_VER_BEGIN_DATA_OBJ_CODE VARCHAR2(64) Y 
OUT_DATA_ID VARCHAR2(200) Y 
OUT_DATA_NAME VARCHAR2(200) Y 
OUT_DATA_SOURCE_ID VARCHAR2(200) Y 
OUT_DATA_SOURCE_CODE VARCHAR2(200) Y 
OUT_OBJ_CODE VARCHAR2(200) Y 
OUT_VER_BEGIN_ID VARCHAR2(200) Y 
OUT_VER_BEGIN_DATA_SOURCE_ID VARCHAR2(200) Y 
OUT_VER_BEGIN_DATA_SOURCE_CODE VARCHAR2(64) Y 
OUT_VER_BEGIN_DATA_OBJ_CODE VARCHAR2(64) Y 
RELA_TYPE_CODE VARCHAR2(200) Y 
MIDDLE_OBJ_CODE VARCHAR2(64) Y 
LAST_CURENT_FLAG CHAR(1) Y 
CREATE_USER_NAME VARCHAR2(200) Y 
CREATE_USER_REAL_NAME VARCHAR2(200) Y 
CREATE_TIME TIMESTAMP(6) Y 
UPDATE_USER_NAME VARCHAR2(200) Y 
UPDATE_USER_REAL_NAME VARCHAR2(200) Y 
UPDATE_TIME TIMESTAMP(6) Y 
DELETE_FLAG CHAR(1) Y 
ORDER_NUM NUMBER(10) Y

另外也有人提到清理索引後,效果會好。 首先,索引有些非技術原因不讓清理。另外我弄了1個新環境,沒有過多索引,試過不同的列上建不同的索引,效果也是一樣的。單就此條SQL來說,過多的索引應該影響不大。

有人問索引狀態,也一併附上:
SQL> select table_name, index_name,index_type,statusfrom user_indexes where table_name='DMS_DATA_RELA';
TABLE_NAME INDEX_NAME INDEX_TYPE STATUS
------------------------------------------------------------ --------------------------- --------
DMS_DATA_RELA OUT_VER_BEGIN_ID_INDEX NORMAL VALID
DMS_DATA_RELA IN_DATA_ID_INDEX NORMAL VALID
DMS_DATA_RELA IN_DATA_NAME_INDEX NORMAL VALID
DMS_DATA_RELA IN_DATA_SOURCE_CODE_INDEX NORMAL VALID
DMS_DATA_RELA IN_DATA_SOURCE_ID_INDEX NORMAL VALID
DMS_DATA_RELA IN_OBJ_CODE_INDEX NORMAL VALID
DMS_DATA_RELA IN_VER_BEGIN_ID_INDEX NORMAL VALID
DMS_DATA_RELA OUT_DATA_NAME_INDEX NORMAL VALID
DMS_DATA_RELA OUT_DATA_SOURCE_ID_INDEX NORMAL VALID
DMS_DATA_RELA OUT_OBJ_CODE_INDEX NORMAL VALID
DMS_DATA_RELA RELA_TYPE_CODE_INDEX NORMAL VALID
DMS_DATA_RELA DELETE_FLAG_INDEX NORMAL VALID
DMS_DATA_RELA LAST_CURENT_FLAG_INDEX NORMAL VALID
DMS_DATA_RELA CREATE_TIME_INDEX FUNCTION-BASED NORMAL VALID
DMS_DATA_RELA OUT_DATA_ID_INDEX NORMAL VALID
DMS_DATA_RELA PK_DMS_DATA_RELA NORMAL VALID
16 rows selected

我試過物化檢視

CREATE MATERIALIZED VIEW mv_dms_ddr
REFRESH force
ON demand
WITH ROWID enable query rewrite AS
SELECT r.OUT_VER_BEGIN_ID, r.IN_DATA_ID,r.IN_VER_BEGIN_ID
FROM DMS_DATA_RELA r, DMS_OBJ o
WHERE r.DELETE_FLAG = '0'
AND r.RELA_TYPE_CODE = 'parent'
AND r.IN_OBJ_CODE != 'o_in'
AND r.OUT_OBJ_CODE =o.OBJ_CODE
AND o.DELETE_FLAG = '0'
AND o.OPEN_STATE = '1'
改寫SQL
SELECT OUT_VER_BEGIN_ID dataID
FROM mv_dms_ddr
START WITH IN_DATA_ID in
(SELECT d.OUT_DATA_ID
FROM DMS_DATA_RELA d
where d.OUT_VER_BEGIN_ID = :1
and d.last_curent_flag = '1')
CONNECT BY PRIOR OUT_VER_BEGIN_ID =IN_VER_BEGIN_ID
(沒用fast更新),帶入變數試了,第1次慢,後面快,執行時間大概是3秒多了。有提升,但不是很理想,而且表更新也頻繁,重新整理方式沒做on commit(我猜測在資料庫變動頻繁的情況下,是不是這個查詢還是會慢,這個想法還沒驗證)

 管理

試過Nested Loop

有人提到nested loop,我前面測過了,效果不怎麼好,現在再把nl的執行計劃附上。執行計劃是代入變數測的。

代入變數值,不加nl的hint

set autot traceonly

SELECT

r.OUT_VER_BEGIN_ID dataID

FROM DMS_DATA_RELA r, DMS_OBJ o

WHERE r.DELETE_FLAG = '0'

AND r.RELA_TYPE_CODE = 'parent'

AND r.OUT_OBJ_CODE = o.OBJ_CODE

AND o.DELETE_FLAG = '0'

AND o.OPEN_STATE = '1'

AND r.IN_OBJ_CODE != 'o_in'

START WITH r.IN_DATA_ID in

(SELECT d.OUT_DATA_ID

FROM DMS_DATA_RELA d

where d.OUT_VER_BEGIN_ID = '20130131036703_syspro_o_wbs'

and d.last_curent_flag = '1')

CONNECT BY r.IN_VER_BEGIN_ID = PRIORr.OUT_VER_BEGIN_ID

已用時間: 00: 00: 06.45

執行計劃

----------------------------------------------------------

Plan hash value: 3423681500

----------------------------------------------------------------------------------------------------

| Id | Operation |Name | Rows | Bytes | Cost (%CPU)| Time |

----------------------------------------------------------------------------------------------------

| 0| SELECT STATEMENT | | 637K| 92M| 6643 (1)| 00:01:20 |

|* 1| FILTER | | | | | |

|* 2| CONNECT BY WITH FILTERING | | | | | |

|* 3| FILTER | | | | | |

| 4| COUNT | | | | | |

|* 5| HASH JOIN | | 637K| 92M| 6643 (1)| 00:01:20 |

| 6| TABLE ACCESS FULL | DMS_OBJ | 63 | 945 | 3 (0)| 00:00:01 |

| 7| TABLE ACCESS FULL | DMS_DATA_RELA | 637K| 83M| 6636 (1)| 00:01:20 |

|* 8| TABLE ACCESS BY INDEX ROWID|DMS_DATA_RELA | 1 | 82 | 4 (0)| 00:00:01 |

|* 9| INDEX RANGE SCAN | OUT_DATA_ID_INDEX | 1 | | 3 (0)| 00:00:01 |

|* 10 | HASH JOIN | | | | | |

| 11| CONNECT BY PUMP | | | | | |

| 12| COUNT | | | | | |

|* 13 | HASH JOIN | | 637K| 92M| 6643 (1)| 00:01:20 |

| 14| TABLE ACCESS FULL | DMS_OBJ | 63 | 945 | 3 (0)| 00:00:01 |

| 15| TABLE ACCESS FULL | DMS_DATA_RELA | 637K| 83M| 6636 (1)| 00:01:20 |

| 16| COUNT | | | | | |

|* 17 | HASH JOIN | | 637K| 92M| 6643 (1)| 00:01:20 |

| 18| TABLE ACCESS FULL | DMS_OBJ | 63 | 945 | 3 (0)| 00:00:01 |

| 19| TABLE ACCESS FULL | DMS_DATA_RELA | 637K| 83M| 6636 (1)| 00:01:20 |

|* 20 | TABLE ACCESS BY INDEX ROWID | DMS_DATA_RELA | 1 | 82 | 4 (0)| 00:00:01 |

|* 21 | INDEX RANGE SCAN |OUT_DATA_ID_INDEX | 1 | | 3 (0)| 00:00:01 |

----------------------------------------------------------------------------------------------------

Predicate Information (identified byoperation id):

---------------------------------------------------

1- filter("R"."DELETE_FLAG"='0' AND"R"."RELA_TYPE_CODE"='parent' AND

"O"."DELETE_FLAG"='0' AND"O"."OPEN_STATE"='1' AND"R"."IN_OBJ_CODE"<>'o_in')

2- filter( EXISTS (SELECT 0 FROM "DMS_DATA_RELA" "D" WHERE"D"."OUT_DATA_ID"=:B1 AND

"D"."OUT_VER_BEGIN_ID"='20130131036703_syspro_o_wbs'AND "D"."LAST_CURENT_FLAG"='1'))

3- filter( EXISTS (SELECT 0 FROM "DMS_DATA_RELA" "D" WHERE"D"."OUT_DATA_ID"=:B1 AND

"D"."OUT_VER_BEGIN_ID"='20130131036703_syspro_o_wbs'AND "D"."LAST_CURENT_FLAG"='1'))

5-access("R"."OUT_OBJ_CODE"="O"."OBJ_CODE")

8-filter("D"."OUT_VER_BEGIN_ID"='20130131036703_syspro_o_wbs'AND

"D"."LAST_CURENT_FLAG"='1')

9- access("D"."OUT_DATA_ID"=:B1)

10- access("R"."IN_VER_BEGIN_ID"=NULL)

13-access("R"."OUT_OBJ_CODE"="O"."OBJ_CODE")

17-access("R"."OUT_OBJ_CODE"="O"."OBJ_CODE")

20-filter("D"."OUT_VER_BEGIN_ID"='20130131036703_syspro_o_wbs'AND

"D"."LAST_CURENT_FLAG"='1')

21- access("D"."OUT_DATA_ID"=:B1)

統計資訊

----------------------------------------------------------

1 recursive calls

0 db block gets

2103709 consistent gets

0 physical reads

0 redo size

596 bytes sent via SQL*Net toclient

350 bytes received via SQL*Netfrom client

2 SQL*Net roundtrips to/fromclient

4 sorts (memory)

0 sorts (disk)

5 rows processed