使用通过 DB Link 访问的 table 的流水线函数
Pipelined function that uses a table accessed via DB Link
我创建了这个流水线函数,用于从 table 中获取配置,该 table 存储在我需要通过数据库访问的数据库中 link:
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.
我试图在我的本地数据库中创建一个指向那个 table 的视图,但它也失败了。此问题的解决方法是什么?
它是 Create Table As Select (CTAS) 与引用远程对象的流水线函数的组合,导致错误“ORA-12840:无法访问远程 table parallel/insert 后直接加载 txn”。 CTAS 语句始终使用称为直接路径写入的优化写入类型,但这些直接路径写入不能很好地处理远程对象。有几种变通方法,例如将您的语句分成单独的 DDL 和 DML 步骤,或使用通用 table 表达式强制 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:无法访问远程 table 之后 parallel/insert 直接加载 txn".
解决方法
避免 CTAS 可能是防止此错误的最直接方法。将 CTAS 直接路径写入隔离在单独的语句中,然后使用将使用“加载 TABLE 常规”操作的常规插入,该操作适用于数据库 links.
--(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') ))
]';
但是如果您想避免重复任何代码,并在一个步骤中完成所有操作,您可以使用通用 Table 表达式 (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 首先检索远程对象的结果并将它们存储在临时 table 中。当 CTAS 被执行时,它从临时 table 中读取并且不再注意到数据库 link。执行计划如下所示:
--------------------------------------------------------------------------------
| 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 |
...
我创建了这个流水线函数,用于从 table 中获取配置,该 table 存储在我需要通过数据库访问的数据库中 link:
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.
我试图在我的本地数据库中创建一个指向那个 table 的视图,但它也失败了。此问题的解决方法是什么?
它是 Create Table As Select (CTAS) 与引用远程对象的流水线函数的组合,导致错误“ORA-12840:无法访问远程 table parallel/insert 后直接加载 txn”。 CTAS 语句始终使用称为直接路径写入的优化写入类型,但这些直接路径写入不能很好地处理远程对象。有几种变通方法,例如将您的语句分成单独的 DDL 和 DML 步骤,或使用通用 table 表达式强制 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:无法访问远程 table 之后 parallel/insert 直接加载 txn".
解决方法
避免 CTAS 可能是防止此错误的最直接方法。将 CTAS 直接路径写入隔离在单独的语句中,然后使用将使用“加载 TABLE 常规”操作的常规插入,该操作适用于数据库 links.
--(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') ))
]';
但是如果您想避免重复任何代码,并在一个步骤中完成所有操作,您可以使用通用 Table 表达式 (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 首先检索远程对象的结果并将它们存储在临时 table 中。当 CTAS 被执行时,它从临时 table 中读取并且不再注意到数据库 link。执行计划如下所示:
--------------------------------------------------------------------------------
| 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 |
...