【问题标题】:Pipelined function that uses a table accessed via DB Link使用通过 DB Link 访问的表的流水线函数
【发布时间】:2023-04-03 02:02:01
【问题描述】:

我创建了这个流水线函数,用于从存储在我需要通过数据库链接访问的数据库中的表中获取配置:

CREATE OR REPLACE FUNCTION fetch_config (
    process_i         IN   VARCHAR2,
    procedure_i       IN   VARCHAR2,
    sub_procedure_i   IN   VARCHAR2
) RETURN t_config_type
    PIPELINED
AS
BEGIN
    FOR r_row IN (
        SELECT
            zprocess,
            zprocedure,
            zsub_procedure,
            zcriteria,
            zfield,
            zfield2,
            zvalue_enabled,
            zenabled
        FROM
            cdc.uap_zufi_dunn_conf@rbip
        WHERE
            zprocess = process_i
            AND zprocedure = procedure_i
            AND zsub_procedure = sub_procedure_i
    ) LOOP
        PIPE ROW ( config_type(r_row.zprocess, r_row.zprocedure, r_row.zsub_procedure, r_row.zcriteria, r_row.zfield,
            r_row.zfield2, r_row.zvalue_enabled, r_row.zenabled) );
    END LOOP;
END fetch_config;

但是,当尝试动态使用该函数时,会抛出以下错误:

BEGIN
    EXECUTE IMMEDIATE q'[
    CREATE TABLE my_table AS
        SELECT
            *
        FROM
            another_table
        WHERE
            cacont_acc IN (
                SELECT
                    zvalue_enabled
                FROM
                    TABLE ( fetch_config('GLOBAL', 'EXCLUSIONS', 'ZBUT000_ATTRIBUTES') ))
    ]'
    ;
END;

错误:

ORA-06512: at line 79
12840. 00000 -  "cannot access a remote table after parallel/insert direct load txn"
*Cause:    Within a transaction, an attempt was made to perform distributed
           access after a PDML or insert direct  statement had been issued.
*Action:   Commit/rollback the PDML transaction first, and then perform
           the distributed access, or perform the distributed access before the
           first PDML statement in the transaction.

我尝试在本地数据库中创建一个指向该表的视图,但它也失败了。解决此问题的方法是什么?

【问题讨论】:

  • 调用管道函数的上下文是什么?还有什么事情?听起来好像有一个开放的事务需要提交。
  • 是的,这就是错误导致相信的,但我已经尝试提交。如果我创建 db 链接表的本地副本并在流水线函数中引用它,它可以工作,因此它必须与该 db 链接有关。

标签: oracle plsql


【解决方案1】:

它是 Create Table As Select (CTAS) 与引用远程对象的管道函数的组合,导致错误“ORA-12840:在并行/插入直接加载 txn 后无法访问远程表”。 CTAS 语句始终使用一种优化的写入类型,称为直接路径写入,但这些直接路径写入不能很好地处理远程对象。有几种解决方法,例如将您的语句分成单独的 DDL 和 DML 步骤,或者使用公用表表达式强制 Oracle 以有效的顺序运行操作。

直接路径写入

下面的代码演示了 CTAS 语句似乎总是使用直接路径写入。常规插入将包含“LOAD TABLE CONVENTIONAL”之类的操作,但直接路径写入显示为“LOAD AS SELECT”操作。

drop table my_table;
explain plan for create table my_table as select 1 a from dual;
select * from table(dbms_xplan.display(format => 'basic'));

Plan hash value: 2781518217
 
-----------------------------------------------------
| Id  | Operation                        | Name     |
-----------------------------------------------------
|   0 | CREATE TABLE STATEMENT           |          |
|   1 |  LOAD AS SELECT                  | MY_TABLE |
|   2 |   OPTIMIZER STATISTICS GATHERING |          |
|   3 |    FAST DUAL                     |          |
-----------------------------------------------------

(但是 - 我不认为 CTAS 使用“真正的”直接路径写入。每次使用真正的直接路径写入都会导致数据问题。必须有一种机制来允许传统写入,但我没有尝试过,例如 NOLOGGING、NOAPPEND 或创建关系约束,能够强制 CTAS 使用“LOAD TABLE CONVENTIONAL”操作。我认为 CTAS 确实使用了介于常规路径和直接路径之间的某种类型的优化。)

直接路径写入针对性能进行了优化,但以牺牲一致性为代价。在提交直接路径写入之前,事务,即使是同一个事务,也不能从同一个对象写入或读取。这通常不是 CTAS 的问题,因为这一切都是一步完成的。但是当有一个远程数据库时,Oracle 不知道该远程数据库正在发生什么样的事务。并且访问远程对象总是会创建一个事务,因此一旦 Oracle 在管道函数中调用远程对象,它就无法知道远程发生了什么并引发“ORA-12840:并行/插入直接加载后无法访问远程表” txn”。

解决方法

避免 CTAS 可能是防止此错误的最直接方法。在单独的语句中隔离 CTAS 直接路径写入,然后使用常规插入,该插入将使用适用于数据库链接的“LOAD TABLE CONVENTIONAL”操作。

    --(Add warning here about not combining the below two statements.)
    EXECUTE IMMEDIATE q'[
    CREATE TABLE my_table AS
        SELECT
            *
        FROM
            another_table
        WHERE
            1=0
    ]';

    EXECUTE IMMEDIATE q'[
    INSERT INTO my_table
        SELECT
            *
        FROM
            another_table
        WHERE
            cacont_acc IN (
                SELECT
                    zvalue_enabled
                FROM
                    TABLE ( fetch_config('GLOBAL', 'EXCLUSIONS', 'ZBUT000_ATTRIBUTES') ))
    ]';

但如果您想避免重复任何代码,并在一个步骤中完成所有操作,您可以使用公用表表达式 (CTE)。

    EXECUTE IMMEDIATE q'[
    CREATE TABLE my_table AS
        WITH configs AS
        (
            --Use CTE and MATERIALIZE hint to avoid ORA-12840.
            SELECT /*+ MATERIALIZE */
                zvalue_enabled
            FROM
                TABLE ( fetch_config('GLOBAL', 'EXCLUSIONS', 'ZBUT000_ATTRIBUTES')
        )
        SELECT
            *
        FROM
            another_table
        WHERE
            cacont_acc IN (SELECT zvalue_enabled FROM configs)
    ]'
    ;

CTE 和 MATERIALIZE 提示强制 Oracle 首先检索远程对象的结果并将它们存储在临时表中。当 CTAS 被执行时,它从临时表中读取,不再注意到数据库链接。执行计划如下所示:

--------------------------------------------------------------------------------
| Id  | Operation                                | Name                        |
--------------------------------------------------------------------------------
|   0 | CREATE TABLE STATEMENT                   |                             |
|   1 |  TEMP TABLE TRANSFORMATION               |                             |
|   2 |   LOAD AS SELECT (CURSOR DURATION MEMORY)| SYS_TEMP_0FD9D6707_8AAECC0C |
|   3 |    COLLECTION ITERATOR PICKLER FETCH     | FETCH_CONFIG                |
|   4 |   LOAD AS SELECT                         | MY_TABLE                    |
...

【讨论】:

  • 非常感谢您的精彩回答。多么大师级的课程!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-30
  • 2023-03-11
  • 2021-07-26
  • 2015-09-05
  • 2017-02-19
  • 1970-01-01
  • 2013-06-06
相关资源
最近更新 更多