How to retrieve the progress of oracle sql query?
Thanks Everyone. I've tried to use OldProgrammer's solution, but failed to do so. Somehow I just can't retrieve Hibernate sesison ID, and all related post I found were either too old or just manually create new session which I don't want.
I did a work around to this problem, not a solution though.
So what I did is that for that complex SQL, first I did total count, then chunk it into 100 chunks. Then with each chunk finish(Pagination), I will update the completion percentage. Meanwhile, on the UI, I will have time-interval to check the progress of this SQL from a different API endpoint, currently I set it to every 2 seconds.
So the outcome is like this, for fairly small amount of rows, it will jump from 2% to 6% for every API call; for large amount, only after few API progress check, it will go from 1% to 2%.
This is not a clean solution, but it did its purpose, it will give User some idea of how long it will take to finish.
Oracle Check Huge Insert Progress
Short answer: There is no database engine which can do that out-of-the-box. You need to understand that before starting the execution, Oracle does not know how many rows will be merged. An option, as stated in the comments, would be to split the query in chunks in PLSQL and use a logging method to monitor rows being committed vs time spent. But that will be much slower than running a normal merge statement, and I can think why it would be worth it.
Nevertheless, this approach can help you somehow. I am assuming your merge statement is not running in parallel for the sake of the purpose. Oracle offers the v$session_longops
dictionary view to monitor long operations, defined as those that might take more than 6 seconds. However, for operation, you must understand each step within an execution plan.
Let's do a PoC
Tables
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.
Let's insert 5M rows in the target table, and 10m in the source table, so that we can issue a merge
for inserting 5M rows.
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.
Let's verify the plan
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")
We then run the merge
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
Meanwhile the process was running, I could get information for the steps
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
Summary
If Oracle thinks any of those steps will take more than 6 seconds, it will include them in the v$session_longops
. You can query this viev to get an estimation per step, but it is not a real measure of how much time the whole DML will take. But it might be a good start.
Another approach, perhaps even better, would be to generate a baseline. You can include the merge in PLSQL, or in any other language for that matter, including a logging method to collect start time, end time and rows merged. Over time and dozens of executions, you can apply statistics and get average and/or standard deviation. I am using a similar approach to generate information of this type that I can use to make educated guesses, nothing more nothing less.
Hope this will help you somehow.
Check status sql query or percentage
Due to the set-based nature of SQL and database processing, you can't really get a "% complete" of a query since the oracle engine doesn't really know for sure. You could try looking at the view v$session_longops to see how far along parts of your SQL has gone (a large hash join or full table scan may show up here). Take a look at this Ask Tom for more info.
If your job has multiple SQL statements and you're trying to track how far along you are after each one, you could add some code to insert status updates on a control table after each statement.
How do I show running processes in Oracle DB?
I suspect you would just want to grab a few columns from V$SESSION and the SQL statement from V$SQL. Assuming you want to exclude the background processes that Oracle itself is running
SELECT sess.process, sess.status, sess.username, sess.schemaname, sql.sql_text
FROM v$session sess,
v$sql sql
WHERE sql.sql_id(+) = sess.sql_id
AND sess.type = 'USER'
The outer join is to handle those sessions that aren't currently active, assuming you want those. You could also get the sql_fulltext column from V$SQL which will have the full SQL statement rather than the first 1000 characters, but that is a CLOB and so likely a bit more complicated to deal with.
Realistically, you probably want to look at everything that is available in V$SESSION because it's likely that you can get a lot more information than SP_WHO provides.
Write an Oracle query that runs for a specific amount of time before returning
You can create pl/sql function and invoke it as part of the query. The function should have the logic to sleep for what ever time you want.
Here is the working code (the schema/user needs to have access on dbms_lock package).
create or replace function func
return number
as
begin
dbms_lock.sleep(60);
return 0;
end;
/
Function created.
SQL> set timing on
SQL> select func from dual;
FUNC
----------
0
Elapsed: 00:01:00.01
Oracle (11.2.0.1) : How to identify the row which is currently updated by the UPDATE statement
You can use the query to monitor long-running DML operations and rollback.
If the update field is not included in the index, then the value of the used_urec
field from the v$transaction
view will be very close to the number of rows.
When the update operation is performed, these values increase, if rollback is performed, the values are reduced to zero.
V$TRANSACTION lists the active transactions in the system.
USED_UREC Number of undo records used
USED_UBLK Number of undo blocks used
select
substr(s.username,1,28) username,
substr(s.program,1,25) program,
s.command,
t.used_urec,
t.used_ublk,
decode(s.command,
0,'No Command',
1,'Create Table',
2,'Insert',
3,'Select',
6,'Update',
7,'Delete',
9,'Create Index',
15,'Alter Table',
21,'Create View',
23,'Validate Index',
35,'Alter Database',
39,'Create Tablespace',
41,'Drop Tablespace',
40,'Alter Tablespace',
53,'Drop User',
62,'Analyze Table',
63,'Analyze Index',
s.command||': Other') command
from
v$session s,
v$process p,
v$transaction t
where s.paddr = p.addr
and s.taddr = t.addr
order by 1
For example 1. If you update a column that is not indexed, then the number of rows 39915830 and USED_UREC 40000562 approximately coincide .
create table test_update(p1,p2,p3,p4 )
PCTFREE 1
INITRANS 1
MAXTRANS 255
TABLESPACE arhiv_data
as
SELECT a.n_p_u, a.id_reg, a.id_vag, a.vrsvop
FROM a_vag_atr a;
SELECT count(*)
FROM test_update a
==>
COUNT(*)
--------------------------------------------
39915830
Session 1
update test_update
set p2=1234567890
==>
39915830 row(s) updated
Session 2
start update
USERNAME PROGRAM COMMAND USED_UREC USED_UBLK COMMAND_1
---------------- ---------------------- ------------------- --------------------
ASUDS sqlnavigator.exe 6 4181959 62690 Update
stop update
USERNAME PROGRAM COMMAND USED_UREC USED_UBLK COMMAND_1
---------------- ---------------------- ------------------- --------------------
ASUDS sqlnavigator.exe 6 40000562 601871 Update
For example 2. if you update the field indexed then the number of lines * 3 is approximately the USED_UREC. 39915830 *3=~116705429
create table test_update(p1,p2,p3,p4 )
PCTFREE 1
INITRANS 1
MAXTRANS 255
TABLESPACE arhiv_data
as
SELECT a.n_p_u, a.id_reg, a.id_vag, a.vrsvop
FROM a_vag_atr a;
SELECT count(*) FROM test_update a
==>
COUNT(*)
--------------------------------------------
39915830
CREATE INDEX test_ind ON test_update
(
p1 ASC
)
Session 1
update test_update
set p1=12
==>
39915830 row(s) updated
Session 2
stop update
USERNAME PROGRAM COMMAND USED_UREC USED_UBLK COMMAND_1
---------------- ---------------------- ------------------- --------------------
ASUDS sqlnavigator.exe 6 116705429 1392538 Update
For example 3. if you insert into table not indexed then the number of rows is exactly the USED_UREC.
create table test_update(p1,p2,p3,p4 )
PCTFREE 1
INITRANS 1
MAXTRANS 255
TABLESPACE arhiv_data
SELECT count(*)
FROM test_update a
==>
COUNT(*)
--------
0
Session 1
declare
i pls_integer:=1;
begin
for i in 1..500000 loop
insert into test_update(p1,p2,p3,p4)
values(1,2,3,sysdate);
end loop;
end;
select count(*) from test_update
==>
COUNT(*)
-----------
500000
Session 2
USERNAME PROGRAM COMMAND USED_UREC USED_UBLK COMMAND_1
ASUDS sqlnavigator.exe 2 500000 5815 Insert
For example 4. if you delete from table not indexed then the number of rows is exactly the USED_UREC.
Session 1
SELECT count(*) FROM test_update a
==>
COUNT(*)
--------
500000
delete from test_update
==>
500000 row(s) deleted
Session 2
USERNAME PROGRAM COMMAND USED_UREC USED_UBLK COMMAND_1
---------------- ---------------------- ------------------- --------------------
ASUDS sqlnavigator.exe 7 500000 9616 Delete
Related Topics
Ora-00054: Resource Busy and Acquire with Nowait Specified
How to Insert Null Values into SQL Server
A How to Escape %% When Building Like Queries in Rails 3/Activerecord
Sql: Last_Value() Returns Wrong Result (But First_Value() Works Fine)
How to Keep Only One Row of a Table, Removing Duplicate Rows
Using SQL Localdb in a Windows Service
SQL Select Max(Date) and Corresponding Value
Update with Case and in - Oracle
Insert of 10 Million Queries Under 10 Minutes in Oracle
SQL Update Records with Row_Number()
SQL 2005 Cte VS Temp Table Performance When Used in Joins of Other Tables
Pass a Table Variable to Sp_Executesql
How to Check If a Value Is a Number in SQLite
Generate_Series in Postgres from Start and End Date in a Table