简短回答:没有数据库引擎可以开箱即用地做到这一点。您需要了解,在开始执行之前,Oracle 不知道将合并多少行。如 cmets 中所述,一种选择是将查询拆分为 PLSQL 中的块,并使用日志记录方法来监视提交的行与所花费的时间。但这比运行普通的合并语句要慢得多,我能想到为什么值得这样做。
不过,这种方法可以以某种方式帮助您。我假设您的合并语句不是为了达到目的而并行运行的。 Oracle 提供v$session_longops 字典视图来监控长时间操作,定义为可能需要超过 6 秒的操作。但是,对于操作,您必须了解执行计划中的每个步骤。
让我们做一个 PoC
表格
SQL> create table test.huge_table ( c1 number, c2 varchar2(40) , c3 varchar2(40) ) ;
Table created.
SQL> create table another_huge_table ( c1 number, c2 varchar2(40) , c3 varchar2(40) ) ;
Table created.
让我们在目标表中插入 5M 行,在源表中插入 10m,这样我们就可以发出 merge 来插入 5M 行。
SQL> declare
2 begin
3 for i in 1 .. 5000000
4 loop
5 insert into test.huge_table
6 values
7 ( i ,
8 lpad(dbms_random.string('A',1),round(dbms_random.value(20,30)),dbms_random.string('A',1)),
9 lpad(dbms_random.string('A',1),round(dbms_random.value(20,30)),dbms_random.string('A',1))
10 );
11 end loop;
12 commit;
13* end;
SQL> /
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats ( ownname => 'TEST' , tabname => 'HUGE_TABLE' , block_sample => true ) ;
PL/SQL procedure successfully completed.
SQL> declare
2 begin
3 for i in 1 .. 10000000
4 loop
5 insert into test.another_huge_table
6 values
7 ( i ,
8 lpad(dbms_random.string('A',1),round(dbms_random.value(20,30)),dbms_random.string('A',1)),
9 lpad(dbms_random.string('A',1),round(dbms_random.value(20,30)),dbms_random.string('A',1))
10 );
11 if i = 5000000
12 then
13 commit ;
14 end if;
15 end loop;
16 commit;
17* end;
SQL> /
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats ( ownname => 'TEST' , tabname => 'ANOTHER_HUGE_TABLE' , block_sample => true ) ;
PL/SQL procedure successfully completed.
让我们验证计划
SQL> explain plan for
merge into test.huge_table t
using (
select c1,c2,c3 from test.another_huge_table
) s
ON (t.c1 = s.c1)
when not matched then
insert (c1, c2, c3)
values ( s.c1, s.c2, s.c3 ); 2 3 4 5 6 7 8 9
Explained.
SQL> select * from table(dbms_xplan.display());
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 4265949913
------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 10M| 1096M| | 67324 (1)| 00:00:06 |
| 1 | MERGE | HUGE_TABLE | | | | | |
| 2 | VIEW | | | | | | |
|* 3 | HASH JOIN RIGHT OUTER| | 10M| 1106M| 333M| 67324 (1)| 00:00:06 |
| 4 | TABLE ACCESS FULL | HUGE_TABLE | 5000K| 276M| | 8232 (1)| 00:00:01 |
| 5 | TABLE ACCESS FULL | ANOTHER_HUGE_TABLE | 10M| 553M| | 16291 (1)| 00:00:02 |
------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("T"."C1"(+)="C1")
然后我们运行合并
SQL> merge into test.huge_table t
using (
select c1,c2,c3 from test.another_huge_table
) s
ON (t.c1 = s.c1)
when not matched then
insert (c1, c2, c3)
values ( s.c1, s.c2, s.c3 ); 2 3 4 5 6 7 8
5000000 rows merged.
Elapsed: 00:00:36.39
SQL> commit ;
Commit complete.
Elapsed: 00:00:00.01
在进程运行的同时,我可以获得有关步骤的信息
SQL> r
1 select opname,
2 target,
3 round( ( sofar/totalwork ), 2 ) * 100 percentage_complete,
4 start_time,
5 ceil( time_remaining / 60 ) max_time_remaining_in_min,
6 floor( elapsed_seconds / 60 ) time_spent_in_min
7 from v$session_longops
8* where sofar != totalwork
OPNAME TARGET PERCENTAGE_COMPLETE START_TIM MAX_TIME_REMAINING_IN_MIN TIME_SPENT_IN_MIN
------------------- --------- ---------------------------------------------------
Table Scan TEST.ANOTHER_HUGE_TABLE 86 28-OCT-21 1 0
OPNAME TARGET PERCENTAGE_COMPLETE START_TIM MAX_TIME_REMAINING_IN_MIN TIME_SPENT_IN_MIN
------------------- --------- ---------------------------------------------------
Hash Join TEST.ANOTHER_HUGE_TABLE 42 28-OCT-21 1 0
OPNAME TARGET PERCENTAGE_COMPLETE START_TIM MAX_TIME_REMAINING_IN_MIN TIME_SPENT_IN_MIN
------------------- --------- ---------------------------------------------------
Hash Join TEST.ANOTHER_HUGE_TABLE 77 28-OCT-21 1 0
总结
如果 Oracle 认为这些步骤中的任何一个将花费超过 6 秒,它会将它们包含在 v$session_longops 中。您可以查询此 viev 以获取每一步的估计值,但这并不是整个 DML 将花费多少时间的真正度量。但这可能是一个好的开始。
另一种可能更好的方法是生成基线。您可以在 PLSQL 或任何其他语言中包含合并,包括收集开始时间、结束时间和合并行的日志记录方法。随着时间的推移和数十次执行,您可以应用统计数据并获得平均值和/或标准偏差。我正在使用类似的方法来生成这种类型的信息,我可以使用这些信息进行有根据的猜测,仅此而已。
希望这会对您有所帮助。