4

我在程序上遇到了一些问题;当运行“大”组(800 多个父母,1300 多个孩子)时,它非常慢(30 - 60 秒)。

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

我解决这个问题的方法是

  1. 为计算值创建带有附加字段的自定义记录类型。
  2. 然后可以将对该记录类型的引用传递给由主处理函数控制的每个函数。
  3. 为每个父记录计算一个值时,将其附加到记录上。

每个过程每次搜索调用一次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)。我插入了包裹,这就是我得到的。

批处理优化器结果 批处理优化器结果

复杂查询 复杂查询

有问题的查询 有问题的查询

4

2 回答 2

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 行(基数 1)。如果不是这种情况,那么查询计划将不是最优的。可能需要收集统计信息、使用动态采样或表函数的基数提示

最后,查看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()); 
于 2014-12-16T23:11:29.753 回答
0

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

于 2013-12-23T16:25:57.357 回答