Thank you for taking the time to take this question and for attempting to recreate it on various versions. Also, thank you for showing me the :LIVESQL_LAST_SQL_ID argument and for The SQL Monitor idea.
Thank you Jeyabal Rajeshwaran for confirming the result on 19.17.
It is understood that a foreign key constraint exists on the child table. This is why it was so surprising that we were being prevented from PDML insert on the parent table apparently due to the foreign key constraint.
I say apparently because the hint says "PDML disabled because *child* referential constraints are present" and because when the child table's constraint is disabled, we do get PDML insert into the parent table.
It is great to know that it is possible to achieve PDML on a referenced (parent) table of an enabled foreign key constraint and that the documentation is correct. However, we are still left trying to determine what it is about our setup that is preventing PDML insert on the parent table.
Yes, we are able to successfully get PDML on table p if we disable the foreign key constraint on table c that references table p.
Because this is not reproducible on Live SQL the script output is provided here as text (at the risk of leaving out something critical, it is quite verbose).
Note: If the session is altered to enable parallel DML and the table p is decorated with the object level PARALLEL clause (thus avoiding the hints), then the the same non-PDML insert result. (results not shown)
Note: hinting optimizer_features_enable('12.2.0.1') still produced the same non-PDML insert results. (results not shown)
Note: I am fairly sure this issue was presenting prior to 19.17; though, I cannot say when exactly we first noticed this.
This example shows that with the FK constraint disabled, then PDML insert into the referenced (parent) table is achieved as verified by v$sql_monitor.
However, if the constraint is enabled, then PDML insert into the referenced (parent) table is not achieved as verified by v$sql_monitor. Please note the 2 second sleep between test cases and sql_exec_start time. The rows from the v$sql_monitor are from the first test case, not the second.
SQL> column name format a33
SQL> column value format a30
SQL> select name, value
2 from v$parameter
3 where name in( 'control_management_pack_access'
4 , 'compatible'
5 , 'cpu_count'
6 , 'optimizer_features_enable'
7 , 'parallel_degree_limit'
8 , 'parallel_degree_policy'
9 , 'parallel_min_time_threshold'
10 , 'parallel_threads_per_cpu'
11 , 'statistics_level'
12 )
13 order by 1
14 ;
NAME VALUE
--------------------------------- ------------------------------
compatible 19.0.0
control_management_pack_access DIAGNOSTIC+TUNING
cpu_count 8
optimizer_features_enable 19.1.0
parallel_degree_limit CPU
parallel_degree_policy MANUAL
parallel_min_time_threshold AUTO
parallel_threads_per_cpu 1
statistics_level TYPICAL
9 rows selected.
SQL> set linesize 171
SQL> set pagesize 0 embedded on
SQL> alter session SET nls_date_format = 'MM/DD/YYYY HH24:MI:SS';
Session altered.
SQL> create table p( x int constraint p_pk primary key );
Table created.
SQL> create table c( x int constraint c_fk references p( x ) );
Table created.
SQL> alter table c modify constraint c_fk disable;
Table altered.
SQL> set feedback on sql_id
SQL> insert /*+ enable_parallel_dml parallel(2) */ into p( x ) select 1 from dual;
1 row created.
SQL_ID: 0ktdd66391zgt
SQL> define my_sql_id = &_sql_id
SQL> set feedback on
SQL> with
2 "M" as
3 ( select sid, sql_id, sql_exec_start, sql_exec_id, sql_plan_hash_value, sql_full_plan_hash_value, process_name, px_servers_requested, px_servers_allocated, px_qcsid
4 , max(sql_exec_id) over() max_sql_exec_id
5 from v$sql_monitor
6 where sql_id = '&my_sql_id'
7 -- order by sql_exec_start desc, sql_exec_id desc, process_name
8 )
9 select sid, sql_id, sql_exec_start, sql_exec_id, sql_plan_hash_value, sql_full_plan_hash_value, process_name, px_servers_requested, px_servers_allocated, px_qcsid
10 from "M"
11 where sql_exec_id = max_sql_exec_id
12 order by process_name
13 ;
old 6: where sql_id = '&my_sql_id'
new 6: where sql_id = '0ktdd66391zgt'
SID SQL_ID SQL_EXEC_START SQL_EXEC_ID SQL_PLAN_HASH_VALUE SQL_FULL_PLAN_HASH_VALUE PROCE PX_SERVERS_REQUESTED PX_SERVERS_ALLOCATED PX_QCSID
---------- ------------- ------------------- ----------- ------------------- ------------------------ ----- -------------------- -------------------- ----------
1385 0ktdd66391zgt 11/19/2022 12:49:57 16777217 232068378 3283043155 ora 4 4
961 0ktdd66391zgt 11/19/2022 12:49:57 16777217 232068378 3283043155 p000 1385
1161 0ktdd66391zgt 11/19/2022 12:49:57 16777217 232068378 3283043155 p001 1385
1398 0ktdd66391zgt 11/19/2022 12:49:57 16777217 232068378 3283043155 p002 1385
1618 0ktdd66391zgt 11/19/2022 12:49:57 16777217 232068378 3283043155 p003 1385
5 rows selected.
SQL> select * from table(dbms_xplan.display_cursor(sql_id => '&my_sql_id',format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'));
old 1: select * from table(dbms_xplan.display_cursor(sql_id => '&my_sql_id',format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'))
new 1: select * from table(dbms_xplan.display_cursor(sql_id => '0ktdd66391zgt',format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'))
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 0ktdd66391zgt, child number 0
-------------------------------------
insert /*+ enable_parallel_dml parallel(2) */ into p( x ) select 1 from
dual
Plan hash value: 232068378
-----------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time | TQ |IN-OUT| PQ Distrib |
-----------------------------------------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | | 2 (100)| | | | |
| 1 | PX COORDINATOR | | | | | | | |
| 2 | PX SEND QC (RANDOM) | :TQ10002 | 1 | 2 (0)| 00:00:01 | Q1,02 | P->S | QC (RAND) |
| 3 | INDEX MAINTENANCE | P | | | | Q1,02 | PCWP | |
| 4 | PX RECEIVE | | 1 | 2 (0)| 00:00:01 | Q1,02 | PCWP | |
| 5 | PX SEND RANGE | :TQ10001 | 1 | 2 (0)| 00:00:01 | Q1,01 | P->P | RANGE |
| 6 | LOAD AS SELECT (HYBRID TSM/HWMB)| P | | | | Q1,01 | PCWP | |
| 7 | OPTIMIZER STATISTICS GATHERING | | 1 | 2 (0)| 00:00:01 | Q1,01 | PCWP | |
| 8 | PX RECEIVE | | 1 | 2 (0)| 00:00:01 | Q1,01 | PCWP | |
| 9 | PX SEND ROUND-ROBIN | :TQ10000 | 1 | 2 (0)| 00:00:01 | Q1,00 | S->P | RND-ROBIN |
| 10 | PX SELECTOR | | | | | Q1,00 | SCWC | |
| 11 | FAST DUAL | | 1 | 2 (0)| 00:00:01 | Q1,00 | SCWP | |
-----------------------------------------------------------------------------------------------------------------------
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1
---------------------------------------------------------------------------
0 - STATEMENT
- parallel(2)
Note
-----
- Degree of Parallelism is 2 because of hint
35 rows selected.
SQL> select * from p;
select * from p
*
ERROR at line 1:
ORA-12838: cannot read/modify an object after modifying it in parallel
SQL> drop table c;
Table dropped.
SQL> drop table p;
Table dropped.
SQL> undefine my_sql_id
SQL>
SQL> exec dbms_session.sleep(2)
PL/SQL procedure successfully completed.
SQL>
SQL> create table p( x int constraint p_pk primary key );
Table created.
SQL> create table c( x int constraint c_fk references p( x ) );
Table created.
SQL> set feedback on sql_id
SQL> insert /*+ enable_parallel_dml parallel(2) */ into p( x ) select 1 from dual;
1 row created.
SQL_ID: 0ktdd66391zgt
SQL> define my_sql_id = &_sql_id
SQL> set feedback on
SQL>
SQL> with
2 "M" as
3 ( select sid, sql_id, sql_exec_start, sql_exec_id, sql_plan_hash_value, sql_full_plan_hash_value, process_name, px_servers_requested, px_servers_allocated, px_qcsid
4 , max(sql_exec_id) over() max_sql_exec_id
5 from v$sql_monitor
6 where sql_id = '&my_sql_id'
7 -- order by sql_exec_start desc, sql_exec_id desc, process_name
8 )
9 select sid, sql_id, sql_exec_start, sql_exec_id, sql_plan_hash_value, sql_full_plan_hash_value, process_name, px_servers_requested, px_servers_allocated, px_qcsid
10 from "M"
11 where sql_exec_id = max_sql_exec_id
12 order by process_name
13 ;
old 6: where sql_id = '&my_sql_id'
new 6: where sql_id = '0ktdd66391zgt'
SID SQL_ID SQL_EXEC_START SQL_EXEC_ID SQL_PLAN_HASH_VALUE SQL_FULL_PLAN_HASH_VALUE PROCE PX_SERVERS_REQUESTED PX_SERVERS_ALLOCATED PX_QCSID
---------- ------------- ------------------- ----------- ------------------- ------------------------ ----- -------------------- -------------------- ----------
1385 0ktdd66391zgt 11/19/2022 12:49:57 16777217 232068378 3283043155 ora 4 4
961 0ktdd66391zgt 11/19/2022 12:49:57 16777217 232068378 3283043155 p000 1385
1161 0ktdd66391zgt 11/19/2022 12:49:57 16777217 232068378 3283043155 p001 1385
1398 0ktdd66391zgt 11/19/2022 12:49:57 16777217 232068378 3283043155 p002 1385
1618 0ktdd66391zgt 11/19/2022 12:49:57 16777217 232068378 3283043155 p003 1385
5 rows selected.
SQL>
SQL> select * from table(dbms_xplan.display_cursor(sql_id => '&my_sql_id',format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'));
old 1: select * from table(dbms_xplan.display_cursor(sql_id => '&my_sql_id',format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'))
new 1: select * from table(dbms_xplan.display_cursor(sql_id => '0ktdd66391zgt',format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'))
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID 0ktdd66391zgt, child number 0
-------------------------------------
insert /*+ enable_parallel_dml parallel(2) */ into p( x ) select 1 from
dual
Plan hash value: 2781518217
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | | 2 (100)| |
| 1 | LOAD AS SELECT | P | | | |
| 2 | OPTIMIZER STATISTICS GATHERING | | 1 | 2 (0)| 00:00:01 |
| 3 | FAST DUAL | | 1 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------
Hint Report (identified by operation id / Query Block Name / Object Alias):
Total hints for statement: 1 (U - Unused (1))
---------------------------------------------------------------------------
0 - STATEMENT
U - parallel(2)
Note
-----
- PDML disabled because child referential constraints are present
27 rows selected.
SQL> select * from p;
select * from p
*
ERROR at line 1:
ORA-12838: cannot read/modify an object after modifying it in parallel
SQL> drop table c;
Table dropped.
SQL> drop table p;
Table dropped.
SQL> undefine my_sql_id
Using parallel_degree_policy MANUAL or LIMITED did not seem to change the result. Using AUTO gave the following two optimizer notes even though the insert was modified to take over one minute and even though all objects involved (except system generated indices to support the primary keys) were decorated with a parallel clause and parallel clauses, if my understanding is correct, are ignored in AUTO mode.
"automatic DOP: Computed Degree of Parallelism is 1 because of parallel threshold" "PDML disabled because object is not decorated with parallel clause"This is the same result regardless if the constraint is disabled or enabled.
SQL> alter session set parallel_degree_policy = AUTO;
Session altered.
SQL> alter session enable parallel dml;
Session altered.
SQL> create table p( x int constraint p_pk primary key, pad varchar2(2000) default 'x' ) parallel 2;
Table created.
SQL> create table c( x int constraint c_fk references p( x ) ) parallel 4;
Table created.
SQL> create table g( x int ) parallel 8;
Table created.
SQL> insert into g(x) with "D" as (select 1 x from dual connect by level <=1000) select 1 from D, D, D where rownum <= 10e7;
100000000 rows created.
SQL> set feedback on sql_id
SQL> --insert into p( x ) with "D" as (select 1 x from dual connect by level <=1000) select rownum from D, D;
SQL> set timing on
SQL> insert into p( x ) select rownum from g;
100000000 rows created.
SQL_ID: d26wthqszhs4j
Elapsed: 00:02:28.01
SQL> set timing off
SQL> define my_sql_id = &_sql_id
SQL> set feedback on
SQL>
SQL> with
2 "M" as
3 ( select sid, sql_id, sql_exec_start, sql_exec_id, sql_plan_hash_value, sql_full_plan_hash_value, process_name, px_servers_requested, px_servers_allocated, px_qcsid
4 , max(sql_exec_id) over() max_sql_exec_id
5 from v$sql_monitor
6 where sql_id = '&my_sql_id'
7 -- order by sql_exec_start desc, sql_exec_id desc, process_name
8 )
9 select sid, sql_id, sql_exec_start, sql_exec_id, sql_plan_hash_value, sql_full_plan_hash_value, process_name, px_servers_requested, px_servers_allocated, px_qcsid
10 from "M"
11 where sql_exec_id = max_sql_exec_id
12 order by process_name
13 ;
old 6: where sql_id = '&my_sql_id'
new 6: where sql_id = 'd26wthqszhs4j'
SID SQL_ID SQL_EXEC_START SQL_EXEC_ID SQL_PLAN_HASH_VALUE SQL_FULL_PLAN_HASH_VALUE PROCE PX_SERVERS_REQUESTED PX_SERVERS_ALLOCATED PX_QCSID
---------- ------------- ------------------- ----------- ------------------- ------------------------ ----- -------------------- -------------------- ----------
1385 d26wthqszhs4j 11/19/2022 13:41:42 16777223 1023046412 3113533584 ora
1 row selected.
SQL>
SQL> select * from table(dbms_xplan.display_cursor(sql_id => '&my_sql_id', format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'));
old 1: select * from table(dbms_xplan.display_cursor(sql_id => '&my_sql_id', format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'))
new 1: select * from table(dbms_xplan.display_cursor(sql_id => 'd26wthqszhs4j', format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'))
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID d26wthqszhs4j, child number 0
-------------------------------------
insert into p( x ) select rownum from g
Plan hash value: 1023046412
-------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | | 42484 (100)| |
| 1 | LOAD TABLE CONVENTIONAL | P | | | |
| 2 | COUNT | | | | |
| 3 | TABLE ACCESS FULL | G | 96M| 42484 (1)| 00:00:02 |
-------------------------------------------------------------------------
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
- automatic DOP: Computed Degree of Parallelism is 1 because of parallel threshold
- PDML disabled because object is not decorated with parallel clause
- Direct Load disabled because no append hint given and not executing in parallel
22 rows selected.
SQL> alter session set parallel_degree_policy = AUTO;
Session altered.
SQL> alter session enable parallel dml;
Session altered.
SQL> create table p( x int constraint p_pk primary key, pad varchar2(2000) default 'x' ) parallel 2;
Table created.
SQL> create table c( x int constraint c_fk references p( x ) ) parallel 4;
Table created.
SQL> alter table c modify constraint c_fk disable;
Table altered.
SQL> create table g( x int ) parallel 8;
Table created.
SQL> insert into g(x) with "D" as (select 1 x from dual connect by level <=1000) select 1 from D, D, D where rownum <= 10e7;
100000000 rows created.
SQL> set feedback on sql_id
SQL> --insert into p( x ) with "D" as (select 1 x from dual connect by level <=1000) select rownum from D, D;
SQL> set timing on
SQL> insert into p( x ) select rownum from g;
100000000 rows created.
SQL_ID: d26wthqszhs4j
Elapsed: 00:02:10.89
SQL> set timing off
SQL> define my_sql_id = &_sql_id
SQL> set feedback on
SQL>
SQL> with
2 "M" as
3 ( select sid, sql_id, sql_exec_start, sql_exec_id, sql_plan_hash_value, sql_full_plan_hash_value, process_name, px_servers_requested, px_servers_allocated, px_qcsid
4 , max(sql_exec_id) over() max_sql_exec_id
5 from v$sql_monitor
6 where sql_id = '&my_sql_id'
7 -- order by sql_exec_start desc, sql_exec_id desc, process_name
8 )
9 select sid, sql_id, sql_exec_start, sql_exec_id, sql_plan_hash_value, sql_full_plan_hash_value, process_name, px_servers_requested, px_servers_allocated, px_qcsid
10 from "M"
11 where sql_exec_id = max_sql_exec_id
12 order by process_name
13 ;
old 6: where sql_id = '&my_sql_id'
new 6: where sql_id = 'd26wthqszhs4j'
SID SQL_ID SQL_EXEC_START SQL_EXEC_ID SQL_PLAN_HASH_VALUE SQL_FULL_PLAN_HASH_VALUE PROCE PX_SERVERS_REQUESTED PX_SERVERS_ALLOCATED PX_QCSID
---------- ------------- ------------------- ----------- ------------------- ------------------------ ----- -------------------- -------------------- ----------
1385 d26wthqszhs4j 11/19/2022 13:54:34 16777225 1023046412 3113533584 ora
1 row selected.
SQL>
SQL> select * from table(dbms_xplan.display_cursor(sql_id => '&my_sql_id', format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'));
old 1: select * from table(dbms_xplan.display_cursor(sql_id => '&my_sql_id', format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'))
new 1: select * from table(dbms_xplan.display_cursor(sql_id => 'd26wthqszhs4j', format => 'ALL LAST +PEEKED_BINDS -PROJECTION -ALIAS'))
PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
SQL_ID d26wthqszhs4j, child number 0
-------------------------------------
insert into p( x ) select rownum from g
Plan hash value: 1023046412
-------------------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | | 42484 (100)| |
| 1 | LOAD TABLE CONVENTIONAL | P | | | |
| 2 | COUNT | | | | |
| 3 | TABLE ACCESS FULL | G | 96M| 42484 (1)| 00:00:02 |
-------------------------------------------------------------------------
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
- automatic DOP: Computed Degree of Parallelism is 1 because of parallel threshold
- PDML disabled because object is not decorated with parallel clause
- Direct Load disabled because no append hint given and not executing in parallel
22 rows selected.