2015-12-21 64 views
1

我有一個Oracle表格表示父子關係,並且我想提高查找祖先記錄的層次結構的查詢性能。我與小數據集在這裏測試,但真正的桌子大得多:具有分層關係的SQL查詢中的性能問題

id name parent_id tagged 
== ==== ========= ====== 
1 One  null  null 
2 Two  1   1 
3 Three 2   null 
4 Four 3   null 
5 Five null  null 
6 Six  5   1 
7 Seven 6   null 
8 Eight null  null 
9 Nine 8   null 

PARENT_ID指回id在同一個表的外鍵關係。

我想編寫一個查詢,它返回每個葉子記錄(那些沒有後代的記錄...在這個例子中是id 4和id 7),它有一個祖先記錄tagged = 1(通過parent_id關係回溯) 。

因此,對於上面的源數據,我想我的查詢返回:

id name tagged_ancestor_id 
== ==== ================== 
4 Four 2 
7 Seven 6 

我目前的查詢檢索這些記錄是:

select * from (
    select id, 
      name, 
      connect_by_root id tagged_ancestor_id  
    from mytree 
    connect by prior id = parent_id 
    start with tagged is not null 
) m1    
where not exists (
    select * from mytree m2 where m2.parent_id = m1.id 
) 

此查詢工作正常在這個簡單的小例如表格,但是在我的真實表格上它的表現非常糟糕,它有大約1100萬條記錄。查詢需要一分鐘才能運行。

  • connect by子句中的兩個字段都有索引。
  • start with子句中的「tagged」字段也有一個索引,我的表中有大約1,500,000條記錄,在這個字段中有非空值。
  • where子句似乎不是問題,因爲修改它以使用where name = 'somename'而不是where not exists ...返回特定名稱(也是索引的)時,查詢仍然需要大約相同的時間量。

那麼,我可以使用什麼策略來嘗試使這些層次結構上的查詢運行得更快?

+0

如果您可以修改模式,則可以重新構建它以存儲修改的預定義樹遍歷,但是需要添加2個附加列(左側和右側值)。幾年前,我在一個我現在無法找到的教程中成功完成了這個任務。 (你應該得到一些額外的性能,但需要花費空間 – ozborn

+0

我確實可以控制模式,因此如果增加一些列是最好的方法來改善這種性能,那麼就可以了。我想我可以把一個標記爲祖先在自己的領域,並做到這一點,但似乎這個查詢可以做得更快。 –

+0

這裏是適當的維基百科參考:https://en.wikipedia.org/wiki/Nested_set_model我不知道我明白在其自己的領域藏匿一個標記的祖先會幫助你(除非你存儲所有這些?),但我可能不理解你的問題...? – ozborn

回答

0

以下是我首先要檢查的內容:

  1. 確保您的表具有主鍵。
  2. 確保表格上的統計數據是最新的。使用DBMS_STATS.GATHER_TABLE_STATS來收集統計信息。看到這個URL:(對於ORACLE版本11。1):

    http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_stats.htm

  3. 即使你有兩個字段的索引個人,你仍然需要

    在2場相結合的指標;在ID和PARENT_ID上創建索引:

    CREATE INDEX on TABLE_NAME(ID,PARENT_ID);

    看到這個網址:

    Optimizing Oracle CONNECT BY when used with WHERE clause

  4. 確保基礎表沒有行鏈接或其他問題(如貪污)。
  5. 確保表和所有索引位於同一個表空間中。

+0

謝謝你的建議,我會檢查出來。 –

0

我不確定這是否更快沒有數據量來測試......但需要考慮的事情。我想我只希望從那些被標記的那些開始,只有那些是葉子的我們正在處理一個較小的體積來處理這可能導致性能增益。但字符串操作的開銷看起來很詭異。

with cte(id, name, parent_id, tagged) as (
SELECT 1, 'ONE', null, null from dual union all 
SELECT 2, 'TWO', 1, 1 from dual union all 
SELECT 3, 'THREE', 2, null from dual union all 
SELECT 4, 'FOUR', 3, null from dual union all 
select 5, 'FIVE', null, null from dual union all 
select 6, 'SIX', 5, 1 from dual union all 
select 7, 'SEVEN', 6, null from dual union all 
select 8, 'EIGHT', null, null from dual union all 
select 9, 'NINE', 8, null from dual), 

Leafs(id, name) as (select id, Name 
      from cte 
where connect_by_isleaf = 1 
Start with parent_Id is null 
connect by nocycle prior id =parent_id), 

Tagged as (SELECT id, name, SYS_CONNECT_BY_PATH(ID, '/') Path, substr(SYS_CONNECT_BY_PATH(ID, '/'),2,instr(SYS_CONNECT_BY_PATH(ID, '/'),'/',2)-2) as Leaf 
from cte 
where tagged=1 
start with id in (select id from leafs) 
connect by nocycle prior parent_id = id) 

select l.*, T.ID as Tagged_ancestor from leafs L 
inner join tagged t 
on l.id = t.leaf 

在本質上我在3 CTE的一個用於數據(CTE)一個用於葉子(葉子)和一個用於記錄標記(標記)

我們遍歷層次的兩倍。一旦獲得所有的葉子,一次獲得所有的標籤。然後,我們從標籤層次結構中解析出第一個葉子值,並將其加回葉子以獲取與標籤記錄相關的葉子。

至於這是不是比你在做什麼,快... 聳肩我不想花時間的測試,因爲我沒有你的指標也沒有告訴我你的數據量