如何优化流水线,弱类型引用游标的使用

时间:2013-04-03 22:45:55

标签: oracle optimization plsql cursor pipelined-function

我在手术方面遇到了一些麻烦;当为“大”集(800多个父母,1300多个孩子)而奔跑时,非常慢(30-60秒)。

基本思想是获取符合特定搜索条件的所有父记录(及其各自的子记录),以及必须计算的3条额外信息。

我解决问题的方法是

  1. 创建自定义记录类型,其中包含计算值的其他字段。
  2. 然后可以将对此记录类型的引用传递给由主处理函数控制的每个函数。
  3. 当为每个父记录计算一个值时,将其添加到记录中。
  4. 每个搜索调用一次GET_PARENT_RECORDSGET_CHILD_RECORDS过程,每个计算函数运行N次(其中N是父记录和/或子记录的数量)。


    问题1 :这是正确的做法吗? (弱类型游标,流水线函数)如果没有,那么我应该如何处理这个问题,假设我可以重新进行?

    问题2 :除非完全重写,否则在提供的代码中是否有明显的改进?

    问题3 :或者其他可能是错误的,因为我注意到,当我运行几次程序时,在20秒内返回相同的慢查询?


    包装定义

    create or replace
    PACKAGE THIS_PKG AS
    
      Type parentCursor IS REF CURSOR;
      Type childCursor IS REF CURSOR;
    
      Type ParentRecordType IS RECORD (
        other_columns,
        Extra_column_A,
        Extra_column_B, 
        Extra_column_C,
        Row_num);    
    
      --associative array
      TYPE ParentArray IS TABLE OF ParentRecordType;
    
      FUNCTION processParents(
          p IN THIS_PKG. parentCursor
      )  RETURN ParentArray
      PIPELINED
      ;
    
      FUNCTION countSomething(some params…)
          RETURN INT;
    
      FUNCTION checkCondX (SomeParent IN ParentRecordType) 
          RETURN VARCHAR2;
    
      FUNCTION checkCondY (SomeParent IN ParentRecordType)
          RETURN VARCHAR2;
    
      PROCEDURE GET_PARENT_RECORDS( other_parameters, Parents OUT THIS_PKG.parentCursor);
    
      PROCEDURE GET_CHILD_RECORDS( other_parameters, Children OUT THIS_PKG.childCursor);
    
    END THIS_PKG;
    

    包裹体

    -- omitted
    
    FUNCTION processParents(
          p IN THIS_PKG.parentCursor
      )  RETURN ParentArray
      PIPELINED
      IS
          out_rec  ParentArray;
          someParent   ParentRecordType;
      BEGIN
        LOOP
            FETCH p BULK COLLECT INTO out_rec LIMIT 100;
    
            FOR i IN 1 .. out_rec.COUNT
            LOOP
            out_rec(i).extra_column_A := countSomething (out_rec(i).field1, out_rec(i).field2);
            out_rec(i).extra_column_B := checkCondX(out_rec(i));
            out_rec(i).extra_column_C := checkCondY(out_rec(i));
            pipe row(out_rec(i));
            END LOOP;
    
            EXIT WHEN p%NOTFOUND;
        END LOOP;
        RETURN;
      END processParents;
    
    PROCEDURE GET_PARENT_RECORDS(
          some_columns,
          Parents OUT THIS_PKG. parentCursor) IS
      BEGIN   
          OPEN Parents FOR
          SELECT *
          FROM TABLE(processParents (CURSOR(
            SELECT *
            FROM (
                  --some select statement with quite a few where clause 
              --to simulate dynamic search (from pre-canned search options)
           )
         ))) abc
          WHERE abc.extra_column_C like '%xyz%' --(xyz is a user given value)
          ;
    END GET_PARENT_RECORDS;
    

    更新 昨天做了一些探索并遇到了Quest Batch SQL Optimizer(来自Toad)。我插上了包裹,这就是我得到的。

    批量优化工具结果 Batch Optimizer results

    复杂查询 Complex query

    有问题的查询 Problematic query

2 个答案:

答案 0 :(得分:1)

行处理部分发生了什么?这些countSomething,checkCondX / Y函数可能会花费很多时间。他们也在拨打SQL电话吗?我首先检查表函数的性能而没有附加谓词。简单地创建一个在SQL而不是函数中执行此操作的查询可能更好 - 如果您可以这样做,它将比为每一行调用函数快得多。

    out_rec(i).extra_column_A := countSomething (out_rec(i).field1, out_rec(i).field2);
    out_rec(i).extra_column_B := checkCondX(out_rec(i));
    out_rec(i).extra_column_C := checkCondY(out_rec(i));

此外,您提供的解释计划很有趣,因为优化器认为所有表中只返回了一行(基数1)。如果不是这种情况,那么查询计划将不是最佳的。可能需要收集统计信息,对表函数使用动态采样或cardinality hints

最后,查看DBMS_SQLTUNE.REPORT_SQL_MONITOR,它提供了有关sql的详细报告。除非查询被动态识别为需要监视您需要添加/ * + MONITOR * /提示。这提供了更多细节,例如返回的行数,执行计数和解释计划中没有的其他有趣的花絮。

SELECT /*+ MONITOR */
FROM slow_query;

-- then run sqltune to get a report
SELECT *
FROM TABLE(DBMS_SQLTUNE.REPORT_SQL_MONITOR()); 

答案 1 :(得分:0)

Quest Batch SQL Optimizer(来自Toad)或任何其他工具将无法帮助您考虑到他们不了解您在函数内部执行的操作。问题出在“FETCH p BULK COLLECT INTO out_rec LIMIT 100;”。传递给p的查询质量实际上定义了最终的执行计划和运行时间。流水线不是缓慢的原因。当您多次运行过程时,Oracle会使用缓存数据。我最好的建议是:为了这个特殊目的,使用Java而不是PL / SQL,理解起来会更简单。