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

Oracle PLSQL: Performance issue when using TABLE functions

我目前在使用 table 函数时遇到性能问题。我会解释的。

我正在使用 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)
    );

然后我有一个 table 这种类型的像:

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() 函数是否可能造成如此大的延迟,而内部只有一条记录?我想知道是否有人可以给我一些建议,告诉我我的方法有什么问题,以及是否有其他方法可以实现...

谢谢!!

此问题可能是由于优化程序对 TABLE 函数编辑的 return 行数的估计不佳造成的。 CARDINALITYDYNAMIC_SAMPLING 提示可能是解决问题的最佳方法。

基数估计

Oracle 收集有关 table 和索引的统计信息,以估计访问这些对象的成本。最重要的估计是一个对象将 return 编辑多少行。默认情况下,过程代码没有统计信息,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 提示

正如现在建议的那样,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 提示

更 "official" 的解决方案是使用 DYNAMIC_SAMPLING 提示。此提示告诉 Oracle 在构建解释计划之前在 运行 时间对一些数据进行采样。这会增加一些构建解释计划的成本,但它会 return 真实的行数。如果您事先不知道数字,这可能会更好。

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 |
--------------------------------------------------------------

但是真正慢的是什么?

我们不知道您的查询到底有多慢。但每当事情进展缓慢时,通常最好关注最差的基数估计。行估计从来都不是完美的,但偏离几个数量级会对执行计划产生巨大影响。在最简单的情况下,它可能会将索引范围扫描更改为完整 table 扫描。