Tuesday, February 03, 2026

Demonstration of Scheduler Jobs (DBMS_SCHEDULER) with Job Class

This post is the second part in a series that discusses the use of job classes with the Oracle database scheduler.
  1. Job Classes and the Database Scheduler
  2. Demonstration of Scheduler Jobs (DBMS_SCHEDULER) with Job Class
  3. Demonstration of Parallel Execution tasks (DBMS_PARALLEL_EXECUTE) with Job Class
This demonstration uses DBMS_SCHEDULER to create a job allocated to a specific resource manager consumer group via a job class.

Job Classes Cannot be Controlled With DBMS_JOB

The DBMS_JOB package was deprecated in Oracle 12c.  One of its limitations is that it cannot specify a job class.  Another reason to use DBMS_SCHEDULER instead, which superceded it.  However, one limited reason to continue using DBMS_JOB is that it avoids the implicit commit in DBMS_SCHEDULER.CREATE_JOB.  Thus, it is possible to create a job (perhaps in a trigger), but defer execution until the current transaction commits.

Demonstration Slow Procedure

The below function runs for several seconds, consuming CPU.  This demonstration then creates jobs that execute this function so that they run for a controllable period of time. 
CREATE OR REPLACE PROCEDURE GFC_BATCH_USER.my_slow(p_n IN INTEGER) IS 
  l_num_rows INTEGER;
BEGIN
  with t as (select /*+MATERIALIZE*/ level n FROM dual CONNECT BY LEVEL <= p_n)
  select count(*) INTO l_num_rows from t
  connect by n > prior n
  start with n = 1;
  dbms_output.put_line('my_slow('||p_n||'):'||l_num_rows||' rows returned');
END;
/
show errors
This procedure runs for about 1 second with a parameter of 23.  Its duration roughly doubles as the parameter is increased by 1.  With a parameter of 29, it runs for about 1 minute.
set timi on serveroutput on
exec GFC_BATCH_USER.my_slow(23);

Create a Job with a Job Class 

The job_class attribute can be specified when a job is created with DBMS_SCHEDULER.CREATE_JOB or altered afterwards with DBMS_SCHEDULER.SET_ATTRIBUTE.
exec dbms_scheduler.drop_job('GFC_BATCH_USER.MY_SLOW_JOB');

BEGIN
  dbms_Scheduler.create_job
  (job_name => 'GFC_BATCH_USER.MY_SLOW_JOB'
  ,job_type => 'STORED_PROCEDURE'
  ,job_action => 'GFC_BATCH_USER.MY_SLOW'
  ,number_of_arguments => 1
  ,job_class => 'SYS.MY_JOB_CLASS'
  ,enabled => FALSE
  ,auto_drop => FALSE
  );
  
  /*alternately attribute can be set separately
  dbms_scheduler.set_attribute 
  (name => 'GFC_BATCH_USER.MY_SLOW_JOB'
  ,attribute=>'JOB_CLASS'
  ,value=>'SYS.MY_JOB_CLASS');*/

  --see https://oracle-base.com/articles/misc/anydata
  dbms_scheduler.set_job_anydata_value 
  (job_name => 'GFC_BATCH_USER.MY_SLOW_JOB'
  ,argument_position => 1
  ,argument_value => SYS.ANYDATA.convertNumber(29) --30=>2 mins
  );
END;
/
Verify the job has been created and that the argument is set correctly.  At the moment, it hasn't run because it is not enabled.
select owner, job_name, job_type, job_action, number_of_arguments, job_class, enabled, auto_drop
from   all_scheduler_jobs
where  job_name = 'MY_SLOW_JOB'
/
select owner, job_name, argument_position, argument_Type
,      SYS.ANYDATA.accessNumber(anydata_value) anydata_value
from   all_scheduler_job_args
where  job_name = 'MY_SLOW_JOB'
/ 

OWNER            JOB_NAME             JOB_TYPE         JOB_ACTION                     Args JOB_CLASS    ENABLED  Drop 
---------------- -------------------- ---------------- ------------------------------ ---- ------------ -------- -----
GFC_BATCH_USER   MY_SLOW_JOB          STORED_PROCEDURE GFC_BATCH_USER.MY_SLOW          1 MY_JOB_CLASS FALSE    TRUE 

                                       Arg                                   
OWNER            JOB_NAME              Pos ARGUMENT_TYPE        ANYDATA_VALUE
---------------- -------------------- ---- -------------------- -------------
GFC_BATCH_USER   MY_SLOW_JOB             1 UNDEFINED                       29

Executing the Job

Finally, enable the job to execute it.  The job is set not to auto-drop on completion, so it can be run many times by repeating this step.
exec dbms_scheduler.enable(name => 'GFC_BATCH_USER.MY_SLOW_JOB');

Verifying the Job is Running

Verify that the job is running and that both the running job record and the session both show the correct consumer group.
select j.owner, j.job_name, j.running_instance, j.session_id, j.resource_Consumer_group, s.module, s.action, s.resource_consumer_group, s.seconds_in_wait
from   all_scheduler_running_jobs j, sys.gv_$session s
where  j.session_id = s.sid and j.running_instance = s.inst_id
/
The resource consumer group on v$session confirms in real-time that it has been set by the job class.
OWNER            JOB_NAME             RUNNING_INSTANCE SESSION_ID RESOURCE_CONSUMER_GROUP
---------------- -------------------- ---------------- ---------- ------------------------
MODULE                           ACTION                           RESOURCE_CONSUMER_GROUP  SECONDS_IN_WAIT
-------------------------------- -------------------------------- ------------------------ ---------------
GFC_BATCH_USER   MY_SLOW_JOB                         2         32 HIGH_BATCH_GROUP         
DBMS_SCHEDULER                   MY_SLOW_JOB                      HIGH_BATCH_GROUP                      26

ALL_SCHEDULER_JOB_LOG

Job Logging shows job class.  
select log_id, log_date, owner, job_name, job_class, status
from   all_scheduler_job_log where job_name = 'MY_SLOW_JOB' 
order by log_date desc
/

LOG_ID LOG_DATE                        OWNER            JOB_NAME             JOB_CLASS            STATUS    
---------- ------------------------------- ---------------- -------------------- -------------------- ----------
   1426216 27/11/25 15.11.43.364349000 GMT GFC_BATCH_USER   MY_SLOW_JOB          MY_JOB_CLASS         SUCCEEDED 
   1424946 27/11/25 08.59.20.795062000 GMT GFC_BATCH_USER   MY_SLOW_JOB          MY_JOB_CLASS         SUCCEEDED 
   1424956 27/11/25 08.57.11.622288000 GMT GFC_BATCH_USER   MY_SLOW_JOB          MY_JOB_CLASS         SUCCEEDED

ALL_SCHEDULER_JOB_RUN_DETAILS

The job run details report start date/time, log date/time, duration, CPU and job output.
select log_id, log_date, owner, job_name, status, instance_id, session_id, actual_start_date, run_duration, cpu_used, output
from   all_scheduler_job_run_details where job_name = 'MY_SLOW_JOB' 
order by log_date desc
/

    LOG_ID LOG_DATE                        OWNER            JOB_NAME             STATUS       ID SESSION_ID  
---------- ------------------------------- ---------------- -------------------- ---------- ---- ------------
ACTUAL_START_DATE                         RUN_DURATION        CPU_USED           
----------------------------------------- ------------------- -------------------
OUTPUT                                                                                                                  
--------------------------------------------------------------------------------------------------------------
   1431896 27/11/25 17.57.23.535680000 GMT GFC_BATCH_USER   MY_SLOW_JOB          SUCCEEDED     2 32,44433     
27/11/25 17.56.19.423438000 EUROPE/LONDON +00 00:01:04.000000 +00 00:01:03.580000 
my_slow(29):268435456 rows returned                                                                                     

   1431862 27/11/25 17.55.20.448915000 GMT GFC_BATCH_USER   MY_SLOW_JOB          SUCCEEDED     1 492,20046    
27/11/25 17.54.16.443151000 EUROPE/LONDON +00 00:01:04.000000 +00 00:01:03.610000 
my_slow(29):268435456 rows returned

Verify Consumer Group in Active Session History

Finally, Active Session History data confirms that the consumer group was switched to HIGH_BATCH_GROUP.   
with h as (
select user_id, session_id, session_serial#, top_level_sql_id, sql_id, consumer_group_id, module, action
,      min(sample_Time) min_Sample_time, max(Sample_Time) max_sample_time
from   gv$active_Session_History h
where  module = 'DBMS_SCHEDULER'
and    action = 'MY_SLOW_JOB'
group by user_id, session_id, session_serial#, top_level_sql_id, sql_id, consumer_group_id, module, action
)
select h.user_id, u.username, h.session_id, h.session_serial#
,      h.consumer_group_id, cg.consumer_group, h.module, h.action, h.min_Sample_time, h.max_sample_time
from   h
  left outer join dba_users u on u.user_id = h.user_id 
  left outer join dba_rsrc_consumer_groups cg on cg.consumer_group_id = h.consumer_Group_id
order by max_sample_time desc
/

                                Session Session                                                                                                                             
   USER_ID USERNAME                  ID Serial# CONSUMER_GROUP_ID CONSUMER_GROUP       MODULE               ACTION               MIN_SAMPLE_TIME       MAX_SAMPLE_TIME      
---------- -------------------- ------- ------- ----------------- -------------------- -------------------- -------------------- --------------------- ---------------------
         0 SYS                       32   44433            738019 HIGH_BATCH_GROUP     DBMS_SCHEDULER       MY_SLOW_JOB          27/11/25 17.56.20.102 27/11/25 17.57.23.116
         0 SYS                      492   20046            738019 HIGH_BATCH_GROUP     DBMS_SCHEDULER       MY_SLOW_JOB          27/11/25 17.54.16.916 27/11/25 17.55.19.932
         0 SYS                      638   46963            738019 HIGH_BATCH_GROUP     DBMS_SCHEDULER       MY_SLOW_JOB          27/11/25 17.47.21.809 27/11/25 17.48.24.888

No comments :