Oracle PLSQL:使用TABLE函数时的性能问题

时间:2015-01-21 14:13:39

标签: oracle performance plsql

使用表函数时,我目前遇到性能问题。我会解释一下。

我正在使用Oracle类型,其中一个定义如下:

create or replace TYPE TYPESTRUCTURE AS OBJECT 
    ( 
        ATTR1       VARCHAR2(30),
        ATTR2       VARCHAR2(20),
        ATTR3       VARCHAR2(20),
        ATTR4       VARCHAR2(20),
        ATTR5       VARCHAR2(20),
        ATTR6       VARCHAR2(20),
        ATTR7       VARCHAR2(20),
        ATTR8       VARCHAR2(20),
        ATTR9       VARCHAR2(20),
        ATTR10      VARCHAR2(20),
        ATTR11      VARCHAR2(20),
        ATTR12      VARCHAR2(20),
        ATTR13      VARCHAR2(10),
        ATTR14      VARCHAR2(50),
        ATTR15      VARCHAR2(13)
    );

然后我有一个这种类型的表,如:

create or replace TYPE TYPESTRUCTURE_ARRAY AS TABLE OF TYPESTRUCTURE ;

我有一个带有以下变量的程序:

  arr QCSTRUCTURE_ARRAY;
  arr2 QCSTRUCTURE_ARRAY;

ARR只包含一个TYPESTRUCTURE实例,其所有属性都设置为NULL,但ATTR4设置为'ABC'

ARR2完全是空的。

这是给我性能问题的部分。

目的是从视图中取一些值(取决于ATTR4上的值)并填充相同或相似结构的值。所以我做了以下几点:

SELECT TYPESTRUCTURE(MV.A,null,null,MV.B,MV.C,MV.D,null,null,MV.E,null,null,MV.F,MV.F,MV.G,MV.H)
BULK COLLECT INTO arr2
FROM TABLE(arr) PARS
JOIN MYVIEW MV
ON MV.B = PARS.ATTR4;

此处的代码正常工作,但执行查询需要15秒的事实......

此查询将在大约20个TYPESTRUCTURE(或行)实例中填入ARR。

看起来视图上可能有很多数据。但让我感到奇怪的是,如果我更改了查询并设置了类似于下面的硬编码,则完全快速(毫秒)

 SELECT TYPESTRUCTURE(MV.A,null,null,MV.B,MV.C,MV.D,null,null,MV.E,null,null,MV.F,MV.F,MV.G,MV.H)
    BULK COLLECT INTO arr2
    FROM (SELECT 'ABC' ATTR4 FROM DUAL) PARS
    JOIN MYVIEW MV
    ON MV.B = PARS.ATTR4;

在这个新查询中,我直接对值进行硬编码,但保留连接以尝试测试与上面的相似但没有TABLE()函数的内容。

所以这里我的问题....这个TABLE()函数是否有可能创建如此大的延迟,内部只有一条记录?我想知道是否有人可以就我的方法中的错误给出一些建议,以及是否可能有其他方法来实现...

谢谢!

1 个答案:

答案 0 :(得分:0)

此问题可能是由优化程序对TABLE函数返回的行数估计不佳引起的。 CARDINALITYDYNAMIC_SAMPLING提示可能是解决问题的最佳方式。

基数估算

Oracle收集有关表和索引的统计信息,以估算访问这些对象的成本。最重要的估计是对象将返回多少行。默认情况下,过程代码没有统计信息,Oracle不会尝试解析代码并估计将生成多少行。每当Oracle看到一个过程行源时,它就会使用一个静态数字。在我的数据库中,数字是16360.在大多数数据库中,估计值是8192,正如beherenow指出的那样。

explain plan for
select * from table(sys.odcinumberlist(1,2,3));

select * from table(dbms_xplan.display(format => 'basic +rows'));

Plan hash value: 2234210431

--------------------------------------------------------------
| Id  | Operation                             | Name | Rows  |
--------------------------------------------------------------
|   0 | SELECT STATEMENT                      |      | 16360 |
|   1 |  COLLECTION ITERATOR CONSTRUCTOR FETCH|      | 16360 |
--------------------------------------------------------------

修复#1:CARDINALITY提示

正如beherenow建议的那样,CARDINALITY提示可以通过静态告知Oracle要估算多少行来解​​决这个问题。

explain plan for
select /*+ cardinality(1) */ * from table(sys.odcinumberlist(1,2,3));

select * from table(dbms_xplan.display(format => 'basic +rows'));


Plan hash value: 2234210431

--------------------------------------------------------------
| Id  | Operation                             | Name | Rows  |
--------------------------------------------------------------
|   0 | SELECT STATEMENT                      |      |     1 |
|   1 |  COLLECTION ITERATOR CONSTRUCTOR FETCH|      | 16360 |
--------------------------------------------------------------

修复#2:DYNAMIC_SAMPLING提示

更“官方”的解决方案是使用DYNAMIC_SAMPLING提示。此提示告诉Oracle在构建解释计划之前在运行时对一些数据进行采样。这会增加构建解释计划的成本,但会返回真正的行数。如果您不提前知道这个数字,这可能会更好。

explain plan for
select /*+ dynamic_sampling(2) */ * from table(sys.odcinumberlist(1,2,3));

select * from table(dbms_xplan.display(format => 'basic +rows'));


Plan hash value: 2234210431

--------------------------------------------------------------
| Id  | Operation                             | Name | Rows  |
--------------------------------------------------------------
|   0 | SELECT STATEMENT                      |      |     3 |
|   1 |  COLLECTION ITERATOR CONSTRUCTOR FETCH|      |     3 |
--------------------------------------------------------------

但是什么真的很慢?

我们不知道您的查询确实很慢。但是每当事情变得缓慢时,通常最好关注最差的基数估计。行估计从来都不是完美的,但是关闭几个数量级会对执行计划产生巨大影响。在最简单的情况下,它可能会将索引范围扫描更改为全表扫描。