If you’re still running Oracle-related batch jobs as OS cron jobs or Windows scheduled tasks, you’re missing one of Oracle’s most underused built-in capabilities. Oracle Scheduler (DBMS_SCHEDULER) has been available since Oracle 10g and in 2025 it’s significantly more capable than most DBAs realize. It provides job scheduling, dependency chains, windowed execution, resource integration, logging, alerting, and remote execution — all manageable through SQL. This post covers what Scheduler can do and how to migrate your cron job chaos into something manageable.
Why DBMS_SCHEDULER Over Cron
The core problem with OS cron jobs for database operations: they’re invisible to the DBA toolset. You can’t see them in Oracle Enterprise Manager. They don’t log to the database. If they fail, the alert goes to an OS mailbox that nobody checks. You can’t chain them with database events. You can’t throttle their resource usage with Resource Manager.
DBMS_SCHEDULER addresses all of these. Every job execution is logged in dba_scheduler_job_run_details. You can query job history. You can set alerts on job failure. You can chain jobs so Job B only runs when Job A succeeds. You can schedule jobs relative to database events. You can run them inside Resource Manager plans to prevent batch jobs from starving OLTP.
Creating Jobs — The Modern Way
sql
-- Simple one-time jobBEGIN DBMS_SCHEDULER.CREATE_JOB( job_name => 'GATHER_STATS_JOB', job_type => 'PLSQL_BLOCK', job_action => 'BEGIN DBMS_STATS.GATHER_SCHEMA_STATS(''MY_SCHEMA''); END;', start_date => SYSTIMESTAMP, enabled => TRUE, comments => 'Gather optimizer statistics for MY_SCHEMA' );END;/-- Recurring job using calendar syntaxBEGIN DBMS_SCHEDULER.CREATE_JOB( job_name => 'NIGHTLY_CLEANUP', job_type => 'STORED_PROCEDURE', job_action => 'MY_SCHEMA.CLEANUP_PROC', repeat_interval => 'FREQ=DAILY; BYHOUR=2; BYMINUTE=0', start_date => TRUNC(SYSDATE+1) + 2/24, enabled => TRUE, auto_drop => FALSE );END;/
The repeat_interval uses iCalendar syntax — expressive and readable. Some useful patterns:
-- Every weekday at 6 AM'FREQ=WEEKLY; BYDAY=MON,TUE,WED,THU,FRI; BYHOUR=6; BYMINUTE=0'-- Every hour during business hours on weekdays'FREQ=HOURLY; BYDAY=MON,TUE,WED,THU,FRI; BYHOUR=8,9,10,11,12,13,14,15,16,17'-- Last day of every month'FREQ=MONTHLY; BYMONTHDAY=-1; BYHOUR=22'-- Every 15 minutes'FREQ=MINUTELY; INTERVAL=15'
Job Chains — Replacing Shell Script Orchestration
Job chains define dependencies between jobs. Job B runs only if Job A succeeded. Job C runs regardless of A’s outcome. Job D runs only if A failed. This replaces the complex shell script logic people build to orchestrate database batch processes.
sql
-- Create a chainBEGIN DBMS_SCHEDULER.CREATE_CHAIN( chain_name => 'ETL_CHAIN', comments => 'Nightly ETL process' );END;/-- Add steps to the chainBEGIN DBMS_SCHEDULER.DEFINE_CHAIN_STEP( chain_name => 'ETL_CHAIN', step_name => 'EXTRACT', program_name => 'EXTRACT_PROGRAM' ); DBMS_SCHEDULER.DEFINE_CHAIN_STEP( chain_name => 'ETL_CHAIN', step_name => 'TRANSFORM', program_name => 'TRANSFORM_PROGRAM' ); DBMS_SCHEDULER.DEFINE_CHAIN_STEP( chain_name => 'ETL_CHAIN', step_name => 'LOAD', program_name => 'LOAD_PROGRAM' );END;/-- Define rules: TRANSFORM runs after EXTRACT succeeds-- LOAD runs after TRANSFORM succeedsBEGIN DBMS_SCHEDULER.DEFINE_CHAIN_RULE( chain_name => 'ETL_CHAIN', rule_name => 'START_EXTRACT', condition => 'TRUE', action => 'START EXTRACT' ); DBMS_SCHEDULER.DEFINE_CHAIN_RULE( chain_name => 'ETL_CHAIN', rule_name => 'RUN_TRANSFORM', condition => 'EXTRACT COMPLETED = TRUE', action => 'START TRANSFORM' ); DBMS_SCHEDULER.DEFINE_CHAIN_RULE( chain_name => 'ETL_CHAIN', rule_name => 'RUN_LOAD', condition => 'TRANSFORM COMPLETED = TRUE', action => 'START LOAD' ); DBMS_SCHEDULER.DEFINE_CHAIN_RULE( chain_name => 'ETL_CHAIN', rule_name => 'END_CHAIN', condition => 'LOAD COMPLETED = TRUE', action => 'END' );END;/
Monitoring and Alerting
sql
-- Check job statusSELECT job_name, state, last_start_date, last_run_duration, next_run_date, failure_count, run_countFROM dba_scheduler_jobsWHERE owner = 'MY_SCHEMA'ORDER BY next_run_date;-- Check recent job historySELECT job_name, status, actual_start_date, run_duration, error#, additional_infoFROM dba_scheduler_job_run_detailsWHERE owner = 'MY_SCHEMA'AND actual_start_date > SYSDATE - 7ORDER BY actual_start_date DESC;-- Find failed jobs in last 24 hoursSELECT job_name, status, error#, additional_info, actual_start_dateFROM dba_scheduler_job_run_detailsWHERE status = 'FAILED'AND actual_start_date > SYSDATE - 1ORDER BY actual_start_date DESC;
For alerting on failures, use DBMS_SCHEDULER notifications:
sql
BEGIN DBMS_SCHEDULER.ADD_JOB_EMAIL_NOTIFICATION( job_name => 'MY_SCHEMA.NIGHTLY_CLEANUP', recipients => 'dba-team@company.com', sender => 'oracle-scheduler@company.com', subject => 'Scheduler Job %job_name% - %event_type%', body => 'Job: %job_name%\nStatus: %event_type%\nError: %error_message%', events => 'JOB_FAILED, JOB_BROKEN' );END;/
Migrating From Cron — The Practical Approach
Don’t migrate everything at once. Prioritize:
- Database maintenance jobs first: statistics gathering, log cleanup, backup verification — these belong in the database anyway
- Jobs that frequently fail silently: anything where you’ve found out about failures only because something downstream broke
- Chained jobs: anything where you have a shell script calling multiple SQL scripts in sequence
Leave OS-level jobs (file system cleanup, OS monitoring) in cron. DBMS_SCHEDULER runs inside Oracle — it’s not the right tool for OS-level operations.

Yorum bırakın