程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> Oracle數據庫 >> Oracle數據庫基礎 >> 使用Oracle樹形查詢需要注意的一些問題

使用Oracle樹形查詢需要注意的一些問題

編輯:Oracle數據庫基礎

Oracle樹形查詢功能,也就是connect by語句使用起來非常方便。樹形結構是很常見的,比如組織機構樹,產品目錄樹等。我們這裡不講述connect by 如何使用,只是提出在使用樹形查詢時需要考慮的一個有關於性能方面的問題。
  這裡提到的問題,主要是Oracle優化器在評估connect by 語句的cardinality時,存在的缺陷,下面將舉例說明。

  在這個例子中所使用的表,是一個真實的生產系統中的表,BSS_ORG: 

SQL> desc bss_org

  名稱 是否為空? 類型

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

  BSS_ORG_ID NOT NULL NUMBER(9)

  NAME NOT NULL VARCHAR2(64)

  BSS_PARENT_ORG_ID NUMBER(9)

  BSS_ORG_LEVEL_ID NOT NULL NUMBER(3)

  STATE NOT NULL VARCHAR2(3)

  STATE_DATE DATE

  BSS_ORG_CODE VARCHAR2(15)
  在這個BSS_ORG表中,BSS_ORG_ID是主鍵,BSS_PARENT_ORG_ID與BSS_ORG_ID形成上下層級關系。這個表的統計信息如下: 

Table Number Empty Average Chain Average Global

  Name of Rows Blocks Blocks Space Count Row Len Stats

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

  BSS_ORG 5,739 52 0 0 0 53 YES

  Column Column Distinct Number

  Name Details Values Density Buckets

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

  BSS_ORG_ID NUMBER(9,0) NOT NULL 5,739 .000174 1

  NAME VARCHAR2(64) NOT NULL 5,034 .000361 200

  BSS_PARENT_ORG_ID NUMBER(9,0) 905 .002189 200

  BSS_ORG_LEVEL_ID NUMBER(3,0) NOT NULL 6 .000087 6

  STATE VARCHAR2(3) NOT NULL 2 .000087 2

  STATE_DATE DATE 1,624 .001434 200

  BSS_ORG_CODE VARCHAR2(15) 5,639 .000179 200

  Table Number Empty Average Chain Average Global

  Name of Rows Blocks Blocks Space Count Row Len Stats

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

  BSS_ORG 5,739 52 0 0 0 53 YES

  Column Column Distinct Number

  Name Details Values Density Buckets

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

  BSS_ORG_ID NUMBER(9,0) NOT NULL 5,739 .000174 1

  NAME VARCHAR2(64) NOT NULL 5,034 .000361 200

  BSS_PARENT_ORG_ID NUMBER(9,0) 905 .002189 200

  BSS_ORG_LEVEL_ID NUMBER(3,0) NOT NULL 6 .000087 6

  STATE VARCHAR2(3) NOT NULL 2 .000087 2

  STATE_DATE DATE 1,624 .001434 200

  BSS_ORG_CODE VARCHAR2(15) 5,639 .000179 200
  下面的測試,是在Oracle 11.1.0.6 for Windows版本下進行的測試。在Oracle 9i、Oracle 10g下測試的結果與Oracle 11g下測試的結果是相符的。當然這裡談到的問題是cardinality,因此在三個版本下,SQL的執行計劃可能有所不同,但最終的結論是一致的。(BTW:從10g開始,connect by語句有一個新的執行步驟,稱為CONNECT BY NO FILTERING,對應的Hint是no_connect_by_filtering)。

Table Number Empty Average Chain Average Global

  Name of Rows Blocks Blocks Space Count Row Len Stats

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

  BSS_ORG 5,739 52 0 0 0 53 YES

  Column Column Distinct Number

  Name Details Values Density Buckets

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

  BSS_ORG_ID NUMBER(9,0) NOT NULL 5,739 .000174 1

  NAME VARCHAR2(64) NOT NULL 5,034 .000361 200

  BSS_PARENT_ORG_ID NUMBER(9,0) 905 .002189 200

  BSS_ORG_LEVEL_ID NUMBER(3,0) NOT NULL 6 .000087 6

  STATE VARCHAR2(3) NOT NULL 2 .000087 2

  STATE_DATE DATE 1,624 .001434 200

  BSS_ORG_CODE VARCHAR2(15) 5,639 .000179 200

  Table Number Empty Average Chain Average Global

  Name of Rows Blocks Blocks Space Count Row Len Stats

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

  BSS_ORG 5,739 52 0 0 0 53 YES

  Column Column Distinct Number

  Name Details Values Density Buckets

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

  BSS_ORG_ID NUMBER(9,0) NOT NULL 5,739 .000174 1

  NAME VARCHAR2(64) NOT NULL 5,034 .000361 200

  BSS_PARENT_ORG_ID NUMBER(9,0) 905 .002189 200

  BSS_ORG_LEVEL_ID NUMBER(3,0) NOT NULL 6 .000087 6

  STATE VARCHAR2(3) NOT NULL 2 .000087 2

  STATE_DATE DATE 1,624 .001434 200

  BSS_ORG_CODE VARCHAR2(15) 5,639 .000179 200
  上面的2條SQL,第1條實際應該返回的行數為5739,第2條SQL實際應該返回的行數為4,但是從執行計劃上看,Oracle優化器評估的行數均為5739。

Table Number Empty Average Chain Average Global

  Name of Rows Blocks Blocks Space Count Row Len Stats

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

  BSS_ORG 5,739 52 0 0 0 53 YES

  Column Column Distinct Number

  Name Details Values Density Buckets

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

  BSS_ORG_ID NUMBER(9,0) NOT NULL 5,739 .000174 1

  NAME VARCHAR2(64) NOT NULL 5,034 .000361 200

  BSS_PARENT_ORG_ID NUMBER(9,0) 905 .002189 200

  BSS_ORG_LEVEL_ID NUMBER(3,0) NOT NULL 6 .000087 6

  STATE VARCHAR2(3) NOT NULL 2 .000087 2

  STATE_DATE DATE 1,624 .001434 200

  BSS_ORG_CODE VARCHAR2(15) 5,639 .000179 200

  Table Number Empty Average Chain Average Global

  Name of Rows Blocks Blocks Space Count Row Len Stats

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

  BSS_ORG 5,739 52 0 0 0 53 YES

  Column Column Distinct Number

  Name Details Values Density Buckets

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

  BSS_ORG_ID NUMBER(9,0) NOT NULL 5,739 .000174 1

  NAME VARCHAR2(64) NOT NULL 5,034 .000361 200

  BSS_PARENT_ORG_ID NUMBER(9,0) 905 .002189 200

  BSS_ORG_LEVEL_ID NUMBER(3,0) NOT NULL 6 .000087 6

  STATE VARCHAR2(3) NOT NULL 2 .000087 2

  STATE_DATE DATE 1,624 .001434 200

  BSS_ORG_CODE VARCHAR2(15) 5,639 .000179 200
  這裡在BSS_PARENT_ORG_ID列上建一個索引,是為了使執行計劃與9i、10g下的一致。

  這2條SQL返回的結果行數,與前面的2條SQL一樣,分別是5739和4,但是從執行計劃上看,Oracle優化器評估出來的行數都是6。

從前面的兩個測試來看,優化器評估出來的SQL返回的行數要麼是5739(表BSS_ORG的總行數),要麼是6(總行數/BSS_PARENT_ORG_ID的Disctint Values)。但無論如何,隨著不同的start with條件,這個行數(cardinality)與實際返回的結果行數可能會存在非常大的差異。如果僅僅是測試中這樣一個簡單的SQL,實際上不會有什麼問題,很容易出現問題的地方在於,一個復雜的SQL中,有類似於測試SQL這樣的子查詢,這樣使得表連接的評估出現很大的偏差,這樣容易引起非常大的性能問題。

  在9i下,如果BSS_PARENT_ORG_ID上如果沒有索引,那麼最後一個測試SQL的執行計劃如下:

 SQL> explain plan for

  2 SELECT *

  3 FROM bss_org t

  4 START WITH bss_org_id = 832044754

  5 CONNECT BY bss_parent_org_id =

  6 PRIOR bss_org_id

  7 ;

  已解釋。

  SQL> @showplan

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

  | Id | Operation | Name | Rows | Bytes | Cost |

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

  | 0 | SELECT STATEMENT | | 5739 | 297K| 9 |

  |* 1 | CONNECT BY WITH FILTERING | | | | |

  | 2 | TABLE Access BY INDEX ROWID| BSS_ORG | | | |

  |* 3 | INDEX UNIQUE SCAN | PK_BSS_ORG | 1 | 7 | 1 |

  | 4 | HASH JOIN | | | | |

  | 5 | CONNECT BY PUMP | | | | |

  | 6 | TABLE Access FULL | BSS_ORG | 5739 | 297K| 9 |

  | 7 | TABLE Access FULL | BSS_ORG | 5739 | 297K| 9 |

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

  Predicate Information (identifIEd by Operation id):

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

  1 - filter("T"."BSS_ORG_ID"=832044754)

  3 - Access("T"."BSS_ORG_ID"=832044754)

  SQL> explain plan for

  2 SELECT *

  3 FROM bss_org t

  4 START WITH bss_org_id = 832044754

  5 CONNECT BY bss_parent_org_id =

  6 PRIOR bss_org_id

  7 ;

  已解釋。

  SQL> @showplan

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

  | Id | Operation | Name | Rows | Bytes | Cost |

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

  | 0 | SELECT STATEMENT | | 5739 | 297K| 9 |

  |* 1 | CONNECT BY WITH FILTERING | | | | |

  | 2 | TABLE Access BY INDEX ROWID| BSS_ORG | | | |

  |* 3 | INDEX UNIQUE SCAN | PK_BSS_ORG | 1 | 7 | 1 |

  | 4 | HASH JOIN | | | | |

  | 5 | CONNECT BY PUMP | | | | |

  | 6 | TABLE Access FULL | BSS_ORG | 5739 | 297K| 9 |

  | 7 | TABLE Access FULL | BSS_ORG | 5739 | 297K| 9 |

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

  Predicate Information (identifIEd by Operation id):

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

  1 - filter("T"."BSS_ORG_ID"=832044754)

  3 - Access("T"."BSS_ORG_ID"=832044754)
  這裡9i的優化器評估出來的cardinality為5739,而11g與此同樣的執行計劃,評估的cardinality是6。

  前段時間就遇上由於connect by語句引起的性能問題。數據庫為Oracle 9208,開始由於bss_org表的bss_parent_org_id列上沒有索引,導致connect by部分得到的cardinality為5739,結果SQL性能非常差,在bss_parent_org_id上建索引後,執行計劃改變,connect by 部分得到的cardinality為6,SQL性能大幅提升。

  對於Oracle優化器不能准確評估connect by 語句的cardinality,目前沒有比較好的解決辦法。必要的時候只有考慮使用Hint了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved