调整复杂查询

时间:2014-09-04 07:48:24

标签: sql oracle performance

我有一个以下查询,需要花费很长时间才能执行。我尝试使用提示并在where子句的列上创建索引,但没有帮助。根据解释计划,总体功能需要更多的成本,对我来说,拥有它们是绝对必要的。无论如何我可以调整它吗?

 INSERT
  INTO tab3
  (CDE,
  SOURCE,
  SCENARIO,
  ID_COUNT,
  AMOUNT)
SELECT /*+ parallel(t,8) */ 'BENEFICIARY' AS CDE, 'MTS' AS SOURCE,'Match on Value' AS SCENARIO,COUNT(T.BA1) AS ID_COUNT,SUM(T.AMT) AS AMOUNT
FROM tab1 E
JOIN tab2 T
ON E.AA1 = T.BA1
WHERE (CASE WHEN E.AF1 = 'Y'
        THEN replace_word(E.AF2)
        ELSE replace_word(E.AF3)
    END) = UPPER(TRIM(T.BF1))
AND E.AF5 = '001'
AND E.AF6 = 'Y'
AND T.BF2 = '001';

如果我将此查询作为过程的一部分并选择查询作为游标,然后使用批量收集插入tab3,那会有帮助吗?在此先感谢您的时间。我们的数据库是Oracle 11g。

修改 添加上述查询的解释计划:

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

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

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

|   0 | INSERT STATEMENT               |                                |     1 |   127 |       |  1458K  (1)| 04:51:40 |

|   1 |  LOAD TABLE CONVENTIONAL       | tab3                           |       |       |       |            |          |

|   2 |   SORT AGGREGATE               |                                |     1 |   127 |       |            |          |

|*  3 |    HASH JOIN                   |                                | 10005 |  1240K|    76M|  1458K  (1)| 04:51:40 |

|*  4 |     TABLE ACCESS BY INDEX ROWID| tab1                           |  1000K|    64M|       |   432K  (1)| 01:26:33 |

|*  5 |      INDEX RANGE SCAN          | IDX_AF5                        |  2000K|       |       |  4483   (1)| 00:00:54 |

|*  6 |     TABLE ACCESS FULL          | AF5                            |  3538K|   199M|       |  1009K  (1)| 03:22:00 |

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


Predicate Information (identified by operation id):

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


   3 - access(CASE "E"."AF1" WHEN 'Y' THEN "REPLACE_WORD"("E"."AF2") ELSE 

              "REPLACE_WORD"("E"."AF3") END =UPPER(TRIM("T"."BF1")) AND 

              "E"."AA1"="T"."BA1")

   4 - filter("E"."AF6"='Y')

   5 - access("E"."AF5"='001')

   6 - filter("T"."BF2"='001')

3 个答案:

答案 0 :(得分:2)

性能调优需要大量信息。此问题包含完整的查询和执行计划,这远远超过大多数性能问题。但正如Rob van Wijk所提到的,重要的是要知道实际的时间和返回的行,而不仅仅是估计。令人惊讶的是,当实数如此接近时,有多少人只根据估计做出猜测。 Rob的方法会起作用,虽然我更喜欢使用  select dbms_sqltune.report_sql_monitor(sql_id => 'your sql_id') from dual;

以下是基于所提供信息的一些提示:

  1. 语句级并行度。将提示SELECT /*+ parallel(t,8) */ ...替换为SELECT /*+ parallel(8) */ ...。当前查询使用对象级提示,它仅指示优化器考虑使用并行性进行一次全表扫描。语句级别提示告诉优化器对整个语句使用并行性。这可以启用并行索引访问。
  2. 并行DML。如果插入了很多行,数据不需要立即恢复,没有锁定问题等,像INSERT /*+ APPEND PARALLEL(8) */ ...这样的提示可能会有所帮助显著。将LOAD TABLE CONVENTIONAL更改为直接路径插入LOAD AS SELECT,有时可以将性能提高几个数量级。让这个工作变得棘手,会话也需要alter session enable parallel dml;,表可能需要NOLOGGING,外键可能需要禁用等等。
  3. replace_word。自定义函数可能会导致SQL语句出现许多问题。如果多次调用,则上下文切换可能会降低性能。根据您的other question,该函数将阻止并行性,因为它未使用PARALLEL_ENABLE定义。并且优化器无法估计函数的选择性,尽管有时可以通过associate statistics来帮助它。如果可能,使用内联视图或其他声明方法替换查询可能更好。此外,你肯定想要删除异常处理程序,除了模糊错误的行号之外什么都不做。
  4. 收集统计数据。正如其他人所说,优化程序统计数据需要准确,以便Oracle建立准确的计划。希望数据库仍然启用默认统计信息收集任务。否则,调用这样的过程通常就足够了:exec dbms_stats.gather_table_stats('schema_name', 'E');。即使我这没有任何改变,这个问题应该始终表明"统计数据是最新的"因为它通常是首先要看的东西。

答案 1 :(得分:0)

摆脱从查询中调用函数:

create or replace function fun1(
  p_var in varchar2
)
return varchar2
is
begin
  return replace(p_var, 'a', 'aaa');
end fun1;

declare
  l_start number := dbms_utility.get_cpu_time;
begin
  for i in (select fun1(object_name) from all_objects where rownum < 10000)
  loop
    null;
  end loop;
  DBMS_OUTPUT.put_line((dbms_utility.get_cpu_time - l_start) || ' hsec');
end;

302 hsec


declare
  l_start number := dbms_utility.get_cpu_time;
begin
  for i in (select replace(object_name, 'a', 'aaa') from all_objects where rownum < 10000)
  loop
    null;
  end loop;
  DBMS_OUTPUT.put_line((dbms_utility.get_cpu_time - l_start) || ' asdf');
end;

268 hsec

正如你所看到的,我有大约0.3秒的利润,这是10K的记录。我每次都跑了好几次蠕虫缓冲区缓存,

答案 2 :(得分:0)

你至少可以做两件事:

  1. 使用 RESULT_CACHE IS(http://www.oracle-developer.net/display.php?id=504

  2. 缓存函数replace_word的结果
  3. 在UPPER上创建功能指数(TRIM(T.BF1))

  4. 根据(http://www.dba-oracle.com/oracle_tips_null_idx.htm)索引null列,你可以这样做:

    create index
    func_idx on
    tab2 T
    (UPPER(TRIM(T.BF1)), 1);
    

    使用pl / sql无济于事,事情可能会比以前更糟糕。

    1. 您可以在插入中尝试APPEND提示。我推荐这个教程:http://www.akadia.com/services/ora_insert_append.html