2014-05-09 60 views
3

我需要幫助強制Oracle始終使用表「r_rapport」(〜60k行)上的索引行ID對錶進行訪問,以便隨後避免對「r_attributfeld」(〜8m行)進行全表掃描。我已導致以下計劃的查詢:如何通過索引rowid在Oracle中強制訪問?

--------------------------------------------------------------------------------------------------------- 
| Id | Operation      | Name   | Rows | Bytes |TempSpc| Cost (%CPU)| Time  | 
--------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT    |    | 101 | 22220 |  | 63518 (2)| 00:12:43 | 
|* 1 | COUNT STOPKEY     |    |  |  |  |   |   | 
| 2 | VIEW       |    | 2870 | 616K|  | 63518 (2)| 00:12:43 | 
|* 3 | SORT ORDER BY STOPKEY  |    | 2870 | 313K| 696K| 63518 (2)| 00:12:43 | 
|* 4 |  FILTER      |    |  |  |  |   |   | 
|* 5 |  HASH JOIN SEMI   |    | 2871 | 314K|  | 51920 (2)| 00:10:24 | 
|* 6 |  HASH JOIN RIGHT SEMI  |    | 2871 | 299K|  | 26084 (2)| 00:05:14 | 
| 7 |  VIEW     | VW_NSO_1  | 214 | 1070 |  |  5 (20)| 00:00:01 | 
|* 8 |   HASH JOIN    |    | 214 | 5350 |  |  5 (20)| 00:00:01 | 
|* 9 |   INDEX RANGE SCAN  | TEST7   | 141 | 1269 |  |  2 (0)| 00:00:01 | 
|* 10 |   INDEX RANGE SCAN  | TEST8   | 228 | 3648 |  |  2 (0)| 00:00:01 | 
|* 11 |  HASH JOIN SEMI   |    | 5848 | 582K|  | 26079 (2)| 00:05:13 | 
|* 12 |   HASH JOIN    |    | 6547 | 620K|  | 243 (2)| 00:00:03 | 
|* 13 |   INDEX RANGE SCAN  | TEST5   | 47 | 470 |  |  2 (0)| 00:00:01 | 
| 14 |   TABLE ACCESS FULL  | R_RAPPORT  | 60730 | 5159K|  | 240 (1)| 00:00:03 | 
| 15 |   VIEW     | VW_SQ_3  | 334K| 1633K|  | 25834 (2)| 00:05:11 | 
|* 16 |   HASH JOIN    |    | 334K| 14M| 10M| 25834 (2)| 00:05:11 | 
| 17 |   INDEX FAST FULL SCAN | TEST4   | 476K| 4656K|  | 368 (2)| 00:00:05 | 
|* 18 |   HASH JOIN   |    | 343K| 11M| 11M| 24214 (2)| 00:04:51 | 
|* 19 |   TABLE ACCESS FULL | R_ATTRIBUTFELD | 343K| 7722K|  | 20483 (2)| 00:04:06 | 
| 20 |   INDEX FAST FULL SCAN| TEST3   | 1670K| 17M|  | 1324 (1)| 00:00:16 | 
| 21 |  VIEW      | VW_SQ_2  | 334K| 1633K|  | 25834 (2)| 00:05:11 | 
|* 22 |  HASH JOIN    |    | 334K| 14M| 10M| 25834 (2)| 00:05:11 | 
| 23 |   INDEX FAST FULL SCAN | TEST4   | 476K| 4656K|  | 368 (2)| 00:00:05 | 
|* 24 |   HASH JOIN    |    | 343K| 11M| 11M| 24214 (2)| 00:04:51 | 
|* 25 |   TABLE ACCESS FULL  | R_ATTRIBUTFELD | 343K| 7722K|  | 20483 (2)| 00:04:06 | 
| 26 |   INDEX FAST FULL SCAN | TEST3   | 1670K| 17M|  | 1324 (1)| 00:00:16 | 
|* 27 |  INDEX RANGE SCAN   | TEST6   |  1 |  8 |  |  8 (0)| 00:00:01 | 
--------------------------------------------------------------------------------------------------------- 

通過添加FIRST_ROWS(1)暗示,這改變爲更期望的計劃:

---------------------------------------------------------------------------------------------------------- 
| Id | Operation       | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
---------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT     |      |  1 | 220 | 96 (0)| 00:00:02 | 
|* 1 | COUNT STOPKEY     |      |  |  |   |   | 
| 2 | VIEW       |      |  1 | 220 | 96 (0)| 00:00:02 | 
|* 3 | FILTER       |      |  |  |   |   | 
| 4 |  NESTED LOOPS     |      |  1 | 97 | 16 (0)| 00:00:01 | 
| 5 |  TABLE ACCESS BY INDEX ROWID | R_RAPPORT   | 60730 | 5159K|  6 (0)| 00:00:01 | 
| 6 |  INDEX FULL SCAN DESCENDING | IDX_R_RAPPORT_3  | 10 |  |  2 (0)| 00:00:01 | 
|* 7 |  INDEX RANGE SCAN    | TEST5    |  1 | 10 |  1 (0)| 00:00:01 | 
| 8 |  NESTED LOOPS     |      |  1 | 25 |  2 (0)| 00:00:01 | 
|* 9 |  INDEX RANGE SCAN    | TEST7    | 141 | 1269 |  2 (0)| 00:00:01 | 
|* 10 |  INDEX UNIQUE SCAN   | TEST8    |  1 | 16 |  0 (0)| 00:00:01 | 
|* 11 |  TABLE ACCESS BY INDEX ROWID | R_ATTRIBUTFELD  |  1 | 23 |  3 (0)| 00:00:01 | 
| 12 |  NESTED LOOPS    |      |  2 | 88 | 35 (0)| 00:00:01 | 
| 13 |   NESTED LOOPS    |      | 10 | 210 |  7 (0)| 00:00:01 | 
|* 14 |   INDEX RANGE SCAN   | TEST4    |  9 | 90 |  3 (0)| 00:00:01 | 
|* 15 |   INDEX RANGE SCAN   | TEST3    |  3 | 33 |  2 (0)| 00:00:01 | 
|* 16 |   INDEX RANGE SCAN   | IDX_R_ATTRIBUTFELD_1 |  6 |  |  2 (0)| 00:00:01 | 
|* 17 |  TABLE ACCESS BY INDEX ROWID| R_ATTRIBUTFELD  |  1 | 23 |  3 (0)| 00:00:01 | 
| 18 |   NESTED LOOPS    |      |  2 | 88 | 35 (0)| 00:00:01 | 
| 19 |   NESTED LOOPS    |      | 10 | 210 |  7 (0)| 00:00:01 | 
|* 20 |   INDEX RANGE SCAN  | TEST4    |  9 | 90 |  3 (0)| 00:00:01 | 
|* 21 |   INDEX RANGE SCAN  | TEST3    |  3 | 33 |  2 (0)| 00:00:01 | 
|* 22 |   INDEX RANGE SCAN   | IDX_R_ATTRIBUTFELD_1 |  6 |  |  2 (0)| 00:00:01 | 
|* 23 |   INDEX RANGE SCAN   | TEST6    |  1 |  8 |  8 (0)| 00:00:01 | 
---------------------------------------------------------------------------------------------------------- 

不幸的是該查詢是由在運行時,如果在這裏有三個子請求而不是兩個,它將忽略FIRST_ROWS提示,並再次對800萬行執行多個全表掃描。對於給定的數據分配,通過rowid訪問總是會更快(幾乎是即時),而Oracle優先選擇的計劃需要幾秒鐘的時間。

在發現那些已被棄用之前,我嘗試在兩個表上使用ROWID提示。

任何指針將不勝感激。

=編輯=

USE_NL &新指數

----------------------------------------------------------------------------------------------------------- 
| Id | Operation       | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
----------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT     |      | 101 | 22220 | 3994 (1)| 00:00:48 | 
|* 1 | COUNT STOPKEY      |      |  |  |   |   | 
| 2 | VIEW        |      | 102 | 22440 | 3994 (1)| 00:00:48 | 
| 3 | NESTED LOOPS SEMI    |      |  1 | 102 | 16 (7)| 00:00:01 | 
| 4 |  NESTED LOOPS     |      |  1 | 97 | 11 (0)| 00:00:01 | 
| 5 |  TABLE ACCESS BY INDEX ROWID | R_RAPPORT   | 58985 | 5011K| 10 (0)| 00:00:01 | 
|* 6 |  INDEX FULL SCAN DESCENDING | TEST12    |  1 |  |  9 (0)| 00:00:01 | 
|* 7 |  TABLE ACCESS BY INDEX ROWID | R_ATTRIBUTFELD  |  1 | 23 |  3 (0)| 00:00:01 | 
| 8 |   NESTED LOOPS    |      |  2 | 88 | 35 (0)| 00:00:01 | 
| 9 |   NESTED LOOPS    |      | 10 | 210 |  7 (0)| 00:00:01 | 
|* 10 |   INDEX RANGE SCAN   | TEST4    |  9 | 90 |  3 (0)| 00:00:01 | 
|* 11 |   INDEX RANGE SCAN   | TEST3    |  3 | 33 |  2 (0)| 00:00:01 | 
|* 12 |   INDEX RANGE SCAN   | IDX_R_ATTRIBUTFELD_1 |  6 |  |  2 (0)| 00:00:01 | 
|* 13 |   TABLE ACCESS BY INDEX ROWID| R_ATTRIBUTFELD  |  1 | 23 |  3 (0)| 00:00:01 | 
| 14 |   NESTED LOOPS    |      |  2 | 88 | 35 (0)| 00:00:01 | 
| 15 |   NESTED LOOPS    |      | 10 | 210 |  7 (0)| 00:00:01 | 
|* 16 |   INDEX RANGE SCAN  | TEST4    |  9 | 90 |  3 (0)| 00:00:01 | 
|* 17 |   INDEX RANGE SCAN  | TEST3    |  3 | 33 |  2 (0)| 00:00:01 | 
|* 18 |   INDEX RANGE SCAN   | IDX_R_ATTRIBUTFELD_1 |  6 |  |  2 (0)| 00:00:01 | 
|* 19 |   INDEX RANGE SCAN   | TEST6    |  1 |  8 |  8 (0)| 00:00:01 | 
|* 20 |  INDEX RANGE SCAN    | TEST5    |  1 | 10 |  1 (0)| 00:00:01 | 
|* 21 |  VIEW       | VW_NSO_1    | 105 | 525 |  5 (20)| 00:00:01 | 
|* 22 |  HASH JOIN      |      | 214 | 5350 |  5 (20)| 00:00:01 | 
|* 23 |  INDEX RANGE SCAN    | TEST7    | 141 | 1269 |  2 (0)| 00:00:01 | 
|* 24 |  INDEX RANGE SCAN    | TEST8    | 228 | 3648 |  2 (0)| 00:00:01 | 
----------------------------------------------------------------------------------------------------------- 

SQL:

select /*+ FIRST_ROWS */ * from ( 
select * 
from r_rapport a 
where rb_id in (
    select obj_id from obj_recht where obj_typ = 20 and obj_pid = 10065 and maske_id in (
     select distinct maske_id 
     from obj_rechtmaske 
     where subj_pid = 10065 
    )) 
and rb_id in (
    select id from rb_buch where pid = 10065 
) 
and exists (
    select /*+ USE_NL(c d) */ 1 
    from r_teilanlage b, r_attribut c, r_attributfeld d 
    where a.id = b.r_id 
    and b.id = c.r_teilanlage_id 
    and c.id = d.r_attribut_id 
    and d.attributfeld_typ not in (20, 25, 40, 78, 79, 90, 92, 123, 124, 125, 126, 127) 
    and lower(d.wert) like lower('%ä%') 
) 
and exists (
    select /*+ USE_NL(c d) */ 1 
    from r_teilanlage b, r_attribut c, r_attributfeld d 
    where a.id = b.r_id 
    and b.id = c.r_teilanlage_id 
    and c.id = d.r_attribut_id 
    and d.attributfeld_typ not in (20, 25, 40, 78, 79, 90, 92, 123, 124, 125, 126, 127) 
    and lower(d.wert) like lower('%ö%') 
) 
and exists (
    select /*+ USE_NL(c d) */ 1 
    from r_teilanlage b, r_attribut c, r_attributfeld d 
    where a.id = b.r_id 
    and b.id = c.r_teilanlage_id 
    and c.id = d.r_attribut_id 
    and d.attributfeld_typ not in (20, 25, 40, 78, 79, 90, 92, 123, 124, 125, 126, 127) 
    and lower(d.wert) like lower('%ä%') 
) 
and exists (
    select /*+ USE_NL(c d) */ 1 
    from r_teilanlage b, r_attribut c, r_attributfeld d 
    where a.id = b.r_id 
    and b.id = c.r_teilanlage_id 
    and c.id = d.r_attribut_id 
    and d.attributfeld_typ not in (20, 25, 40, 78, 79, 90, 92, 123, 124, 125, 126, 127) 
    and lower(d.wert) like lower('%ö%') 
) 
and a.id not in (
    select r_id from r_gelesen where ma_id = 144 
) 
order by a.open_stamp desc 
) where rownum <= 101; 

和計劃:

---------------------------------------------------------------------------------------------------------- 
| Id | Operation       | Name     | Rows | Bytes | Cost (%CPU)| Time  | 
---------------------------------------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT     |      |  1 | 220 | 1195K (1)| 03:59:08 | 
|* 1 | COUNT STOPKEY     |      |  |  |   |   | 
| 2 | VIEW       |      |  1 | 220 | 1195K (1)| 03:59:08 | 
|* 3 | FILTER       |      |  |  |   |   | 
| 4 |  NESTED LOOPS SEMI    |      | 3213 | 320K| 1018K (1)| 03:23:47 | 
| 5 |  NESTED LOOPS     |      | 6547 | 620K| 82249 (1)| 00:16:27 | 
| 6 |  TABLE ACCESS BY INDEX ROWID | R_RAPPORT   | 60730 | 5159K| 21493 (1)| 00:04:18 | 
| 7 |  INDEX FULL SCAN DESCENDING | IDX_R_RAPPORT_3  | 60730 |  | 152 (1)| 00:00:02 | 
|* 8 |  INDEX RANGE SCAN   | TEST5    |  1 | 10 |  1 (0)| 00:00:01 | 
|* 9 |  VIEW       | VW_NSO_1    | 105 | 525 | 143 (0)| 00:00:02 | 
| 10 |  NESTED LOOPS    |      | 214 | 5350 | 143 (0)| 00:00:02 | 
|* 11 |  INDEX RANGE SCAN   | TEST7    | 141 | 1269 |  2 (0)| 00:00:01 | 
|* 12 |  INDEX RANGE SCAN   | TEST8    |  2 | 32 |  1 (0)| 00:00:01 | 
|* 13 |  TABLE ACCESS BY INDEX ROWID | R_ATTRIBUTFELD  |  1 | 23 |  3 (0)| 00:00:01 | 
| 14 |  NESTED LOOPS     |      |  6 | 264 | 97 (0)| 00:00:02 | 
| 15 |  NESTED LOOPS    |      | 30 | 630 | 13 (0)| 00:00:01 | 
|* 16 |  INDEX RANGE SCAN   | TEST4    |  9 | 90 |  3 (0)| 00:00:01 | 
|* 17 |  INDEX RANGE SCAN   | TEST3    |  3 | 33 |  2 (0)| 00:00:01 | 
|* 18 |  INDEX RANGE SCAN   | IDX_R_ATTRIBUTFELD_1 |  6 |  |  2 (0)| 00:00:01 | 
|* 19 |  TABLE ACCESS BY INDEX ROWID | R_ATTRIBUTFELD  |  1 | 23 |  3 (0)| 00:00:01 | 
| 20 |  NESTED LOOPS    |      |  6 | 264 | 97 (0)| 00:00:02 | 
| 21 |  NESTED LOOPS    |      | 30 | 630 | 13 (0)| 00:00:01 | 
|* 22 |   INDEX RANGE SCAN   | TEST4    |  9 | 90 |  3 (0)| 00:00:01 | 
|* 23 |   INDEX RANGE SCAN   | TEST3    |  3 | 33 |  2 (0)| 00:00:01 | 
|* 24 |  INDEX RANGE SCAN   | IDX_R_ATTRIBUTFELD_1 |  6 |  |  2 (0)| 00:00:01 | 
|* 25 |  TABLE ACCESS BY INDEX ROWID | R_ATTRIBUTFELD  |  1 | 23 |  3 (0)| 00:00:01 | 
| 26 |  NESTED LOOPS    |      |  6 | 264 | 97 (0)| 00:00:02 | 
| 27 |   NESTED LOOPS    |      | 30 | 630 | 13 (0)| 00:00:01 | 
|* 28 |   INDEX RANGE SCAN   | TEST4    |  9 | 90 |  3 (0)| 00:00:01 | 
|* 29 |   INDEX RANGE SCAN   | TEST3    |  3 | 33 |  2 (0)| 00:00:01 | 
|* 30 |   INDEX RANGE SCAN   | IDX_R_ATTRIBUTFELD_1 |  6 |  |  2 (0)| 00:00:01 | 
|* 31 |  TABLE ACCESS BY INDEX ROWID| R_ATTRIBUTFELD  |  1 | 23 |  3 (0)| 00:00:01 | 
| 32 |   NESTED LOOPS    |      |  6 | 264 | 97 (0)| 00:00:02 | 
| 33 |   NESTED LOOPS    |      | 30 | 630 | 13 (0)| 00:00:01 | 
|* 34 |   INDEX RANGE SCAN  | TEST4    |  9 | 90 |  3 (0)| 00:00:01 | 
|* 35 |   INDEX RANGE SCAN  | TEST3    |  3 | 33 |  2 (0)| 00:00:01 | 
|* 36 |   INDEX RANGE SCAN   | IDX_R_ATTRIBUTFELD_1 |  6 |  |  2 (0)| 00:00:01 | 
|* 37 |   INDEX RANGE SCAN   | TEST6    |  1 |  8 |  8 (0)| 00:00:01 | 
---------------------------------------------------------------------------------------------------------- 

一個可怕的,可怕的成本EST因爲這個查詢立即完成。

+3

如果你想強制使用索引,我會從'INDEX'提示開始,但退一步說,您確定無法通過修復統計信息中的任何不準確情況來解決問題會導致優化器相信較慢的計劃會更有效嗎?特別是如果您動態組裝查詢,指定單個提示可能會帶來相當大的問題。 –

+0

@JustinCave - 所有優點:您可能希望確保表格統計信息是最新的/準確的。舊數據庫允許基於規則的優化「強制」。什麼是Oracle版本? –

+0

正在使用的Oracle版本是10.2。我嘗試了各種索引提示,這些提示沒有做任何事情:/ Afair統計數據定期更新,但我沒有權利自己做;將不得不檢查。 – Thaylon

回答

1

如果你想避免在r_attributfeld上進行表訪問,你應該在這個表上嘗試一個嵌套循環。 也許你必須有一個龍頭(r_rapport r_attributfeld)(或ordered提示)如果甲骨文「不明白」你想

+0

加入訂單似乎沒有問題給我,是不是在兩個計劃基本相同,除了良好的使用索引rowid訪問和醜陋的全表掃描?無論如何,謝謝,下週一會嘗試。 – Thaylon

+0

如果我理解你的問題,r_rapport是主要的表格。如果r_attributfeld索引良好,我相信嵌套循環將是解決方案。我們可以有你的查詢的全文和表格的描述嗎?當然,它會更復雜,因爲你的桌子似乎在視野內。也許爲了我們需要一個推 - 提示的提示。但這是猜測。沒有文字,就很難找到一些東西! – eliatou

+0

謝謝大家!最外面的查詢中的FIRST_ROWS(1)和最裏面的子查詢中的USE_NL(c d)的組合是解決方案。將接受這個答案,指出我可以做一個嵌套的循環提示,我沒有意識到。 – Thaylon

-1

什麼並不像你認爲是更好的辦法是真的更好的計劃。快速返回第一行是使用嵌套循環和rowid訪問的正確方法。但是,如果你想快速返回所有結果集,即使你通過rowid(「select * from tab where rowid in(...)」或「... tab.rowid = ...')所以我認爲Oracle爲你選擇了正確的計劃