2014-05-09 32 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为你选择了正确的计划