All about
Database Jobs and Intervals
Introduction
The DBMS_JOB package allows a user to schedule a job to run at a
specified
time. A job is submitted to a job queue and runs at the specified
time. The user can also input a parameter that specifies how
often the job should run. A job can consist of any PL/SQL code.
SNP Background Processes
SNP processes run in the background and implement database snapshots
and job queues. If an SNP process fails, Oracle restarts it
without affecting the rest of the database. An SNP process can
run one job at a time with a maximum of ten SNP processes running
simultaneously. The INIT.ORA initialization file contains three
parameters that control the behavior of the SNP processes:
Parameters |
Description |
JOB_QUEUE_PROCESSES |
How many processes to start. If set to zero, no jobs are executed. Default is 0. Range is 0..10. |
JOB_QUEUE_INTERVAL (obsolete in 9i) |
How long an interval the process will sleep before checking for a new job. Default is 60 sec. Range is 1..3600 sec |
JOB_QUEUE_KEEP_CONNECTIONS |
Controls whether an SNP process closes any remote database connections. Default is False. Range is True/False. |
Quick overview of Oracle_Jobs
Create a Job (run it every day)
VARIABLE
v_jobnum NUMBER;
BEGIN
dbms_job.submit(:v_jobnum, 'MY_STORED_PROCEDURE;', sysdate,'sysdate+1');
END;
Run an Existing Job
exec
dbms_job.run(job_number);
Remove a Job from the Job Queue
exec
dbms_job.remove(job_number);
Review Job Status
Column Job# format a4
Column What format a27
Column Last_Date format a15
Column Next_Date format a15
select substr(to_char(job),1,3)
Job#,
substr(what,1,27) What,
to_char(last_date, 'dd-MON-YY
HH24:MI') Last_Date,
to_char(Next_Date, 'dd-MON-YY
HH24:MI') Next_Date, failures fail#,
total_time
from user_jobs;
Examples
Monday through Friday Example
The following example shows how to schedule a job to execute Monday
through Friday:
DBMS_JOB.SUBMIT
(:v_jobnum,'my_procedure;', sysdate,
'TRUNC(LEAST(NEXT_DAY(SYSDATE,''MONDAY''),
NEXT_DAY(SYSDATE,''TUESDAY''),
NEXT_DAY(SYSDATE,''WEDNESDAY''),
NEXT_DAY(SYSDATE,''THURSDAY''),
NEXT_DAY(SYSDATE,''FRIDAY'') ))');
The following example shows how to schedule a job to execute Monday
through Friday at 6:00 pm:
DBMS_JOB.SUBMIT
(:v_jobnum,'my_procedure;', sysdate,
'TRUNC(LEAST(NEXT_DAY(SYSDATE,''MONDAY''),
NEXT_DAY(SYSDATE,''TUESDAY''),
NEXT_DAY(SYSDATE,''WEDNESDAY''),
NEXT_DAY(SYSDATE,''THURSDAY''),
NEXT_DAY(SYSDATE,''FRIDAY'') )) + 18/24');
Notes:
** Two single quotes are used around the day of the week, not double
quotes.
** Ensure that the 4th parameter is all on one line. Allow the
line to wrap if necessary. The 4th parameter only allows 200
characters. Several white spaces can cause this limit to be reached
quickly.
Midnight Example
The following example shows how to schedule a job to execute at
midnight every night:
DBMS_JOB.SUBMIT (:v_jobnum,
'my_procedure;', trunc(sysdate) +
1,'sysdate + 1');
The 'trunc(sysdate) + 1' sets the time back to the current day,
midnight, and informs the scheduler to start the job at the following
midnight.
The 'sysdate + 1' sets the interval to 1 day. This job would run
the first time at midnight, then every midnight afterwards.
To schedule the job at 8 a.m., specify the next_date parameter as
trunc(sysdate) + 1 + 8/24.
Daily Example
The following example shows how to schedule a job to execute every day
at the same time:
DBMS_JOB.SUBMIT (:v_jobnum,
'my_procedure;',
trunc(sysdate)+?.??/24,'trunc(sysdate+1)+?.??/24');
To run the job at the same time every day, make sure that you pass the same value '?.??/24' to both the 'next_date' and 'interval' parameters. The value returned by trunc(sysdate) is midnight today and the value returned by trunc(sysdate + 1) is midnight tomorrow.
Hourly Example
If the interval between execution should be 1 hour, then the job must
be submitted as
EXEC
DBMS_JOB.SUBMIT(:jobno,'MY_PROCEDURE;', SYSDATE, 'SYSDATE +
1/24');
To specify the time, say every half past hour the procedure has to
execute,
dbms_job.submit(:jobno,'my_procedure;',to_date('28/nov/00 15:30:00',
'dd/mon/yy HHH4:MI:SS'),'sysdate+1/24');
Start at Specific Time and run it every Sunday at 9 AM
DBMS_JOB.SUBMIT (:v_jobnum,
'MY_PROCEDURE;',
TO_DATE('0409200009','DDMMYYYYHH24'),
'NEXT_DAY(TRUNC(SYSDATE),''SUNDAY'')');
or
DBMS_JOB.SUBMIT (:v_jobnum,
'MY_PROCEDURE;', to_date('15-Aug-2002
12:00','dd-Mon-yyyy hh24:mi'),
'NEXT_DAY(TRUNC(SYSDATE),''SUNDAY'')');
Example of Monthly execution
dbms_job.submit(:v_jobnum,
'PROCEDURE;', to_date('15-Aug-2002
12:00','dd-Mon-yyyy hh24:mi'),
sysdate+to_char(last_day(sysdate),'dd'));
Restore a Broken Job
exec dbms_job.broken(#, false);
commit;
CAUTION!! with INTERVAL
The interval part of the job is analyzed AFTER the
job is
executed. If you want the job to run at the top of the hour, and
you specified an interval of 'sysdate+1/24'... recognize that the value
for sysdate is going to be evaluated AFTER the job completes... so if
the elapsed time of the job is 5 minutes, the job will be executed at
times like 1:00, 2:05, 3:10, 4:15, 5:20, etc.
If you want the job to run on the hour, every hour, then you need to
account for a possible delay in the start of the job, as well as the
elapsed run time of the job in the value of sysdate.
Just make sure that expression used for the interval results in the
value that you want e.g. to schedule a job to run at the top of each
hour, specify interval as 'trunc(sysdate,''HH24'')+1/24'.
Examples:
Run every day at 8.00 in the morning:
dbms_job.submit(:jno,'xyz;',trunc(sysdate)+1/3,'trunc(sysdate+1)+1/3') ;
Run every day, every
hour at the top of the hour starting on the next top of the hour:
dbms_job.submit(:jno,'xyz;',
trunc(sysdate)+to_char(sysdate,'hh')/24+1/24 ,
'trunc(sysdate,''hh'')+2/24' ) ;
or
DBMS_JOB.SUBMIT (:v_jobnum,
'my_procedure;', trunc(sysdate), 'trunc(sysdate,''hh'')+1/24' )
Import/Export:
Jobs can be imported and exported. If a job is defined in one
database, it can be transferred to another database. This does
not change the job number, environment, or definition.
Job Owner:
The person who submits a job to the job queue is considered the job's
owner. The job's owner is the only one who can alter the job, force the
job to run,
or remove the job from the queue. If you need to look up any
information regarding a submitted job, DBA_JOBS and USER_JOBS contain a
lot of
information such as the job number, user, status, etc.
Job Definitions:
The WHAT parameter of the SUBMIT procedure specifies the job
definition. The WHAT parameter should be a string that calls the stored
procedure to
be run. There can be any number of parameters. All
parameters should be IN parameters. The only exceptions to this
rule are the special identifiers next_date and broken. Job
definitions should be enclosed in single quotes and terminated with a
semicolon.
DBMS_JOB.SUBMIT(1234,
'employee_stats(''PROGRAMMER'',''FULL_TIME'',''HIRE_DATE'');',
SYSDATE,
SYSDATE + 7);
Job execution intervals are determined by the date expression set by the interval parameter. One key to setting the interval correctly is determine which of the following applies to the job:Job Intervals Examples
- Each execution of the job should follow the last by a specific time interval.
- The job should execute on specific dates and times.
Type 1 Jobs
Jobs of type 1 usually have relatively simple date arithmetic expressions of the type SYSDATE+N, where N represents the time interval expressed in days. The following table provides examples of these types of intervals.
Action Interval Time Execute daily 'SYSDATE + 1' Execute every 4 hours 'SYSDATE + 4/24' Execute every 10 minutes 'SYSDATE + 10/1440' Execute every 30 seconds 'SYSDATE + 30/86400' Execute every 7 days 'SYSDATE + 7' Do no re-execute and remove job NULL
NOTE: Remember that job intervals expressed as shown in the previous table do not guarantee that the next execution will happen at a specific day or time, only that the spacing between executions will be at least that specified. For instance, if a job is first executed at 12:00 p.m. with in interval of 'SYSTEM + 1', it will be scheduled to execute the next day at 12:00 p.m. However, if a user executes the job manually at 4:00 p.m. the next day using DBMS_JOB.RUN, then it will be rescheduled for execution at 4:00 p.m. the next day. Another possibility is that the database is down or the job queue so busy that the job cannot be executed exactly at the time scheduled. In this case, the job will run as soon as it can, but the execution time will have migrated away from the original submission time due to the later execution. This "drift" in next execution times is characteristic of jobs with simple interval expressions. Type 2 Jobs
Jobs with type 2 execution requirements involve more complex interval date expressions, as see in the following table.
Action Interval Time Every day at 12:00 midnight TRUNC(SYSDATE + 1) Every day at 8:00 p.m. TRUNC(SYSDATE + 1) + 20/24 Every Tuesday at 12:00 noon NEXT_DAY(TRUNC(SYSDATE), "TUESDAY") + 12/24 First day of the month at midnight TRUNC(LAST_DAY(SYSDATE) + 1) Last day of the quarter at 11:00 p.m. TRUNC(ADD_MONTH(SYSDATE + 2/24,3),'Q') - 1/24 Every Monday, Wednesday and Friday at 9:00 p.m. TRUNC(LEAST(NEXT_DAY(SYSDATE, "MONDAY"), NEXT_DAY(SYSDATE, "WEDNESDAY"), NEXT_DAY(SYSDATE, "FRIDAY"))) + 21/24
Remember that the dbms_job.submit() procedure accepts three parameters:Oracle Date Math Examples
- the name of the job to submit
- the start time for the job
- and the interval to execute the job
DBMS_JOB.SUBMIT (
job => :jobno
, what => 'statspack.snap;' -- What to run
, next_date => TRUNC(sysdate+1/24,'HH') -- Start next hour
, interval => 'TRUNC(SYSDATE+1/24,''HH'')' -- Run every hour
);-- =========================================================
-- Schedule a SNAPSHOT to be run on this instance every hour
-- =========================================================
VARIABLE jobno NUMBER;
VARIABLE instno NUMBER;
BEGIN
select instance_number into :instno from v$instance;
-- ------------------------------------------------------------
-- Submit job to begin at 0600 and run every hour.
-- ------------------------------------------------------------
DBMS_JOB.SUBMIT (
:jobno
, 'statspack.snap;'
, TRUNC(sysdate+1)+6/24
, 'TRUNC(SYSDATE+1/24,''HH'')'
, TRUE
, :instno);
-- ------------------------------------------------------------
-- Submit job to begin at 0900 and run 12 hours later
-- ------------------------------------------------------------
DBMS_JOB.SUBMIT (
:jobno
, 'statspack.snap;'
, TRUNC(sysdate+1)+9/24
, 'TRUNC(SYSDATE+12/24,''HH'')'
, TRUE
, :instno);
-- ------------------------------------------------------------
-- Submit job to begin at 0600 and run every 10 minutes
-- ------------------------------------------------------------
DBMS_JOB.SUBMIT (
:jobno
, 'statspack.snap;'
, TRUNC(sysdate+1)+6/24
, 'TRUNC(sysdate+10/1440,''MI'')'
, TRUE
, :instno);
-- ----------------------------------------------------------------
-- Submit job to begin at 0600 and run every hour, Monday - Friday
-- ----------------------------------------------------------------
DBMS_JOB.SUBMIT (
:jobno
, 'statspack.snap;'
, TRUNC(sysdate+1)+6/24
, 'TRUNC(
LEAST(
NEXT_DAY(sysdate,''MONDAY'')
, NEXT_DAY(sysdate,''TUESDAY'')
, NEXT_DAY(sysdate,''WEDNESDAY'')
, NEXT_DAY(sysdate,''THURSDAY'')
, NEXT_DAY(sysdate,''FRIDAY'')
) + 1/24
, ''HH'')'
, TRUE
, :instno);
COMMIT;
END;
/
Run Statspack Snapshot Every 15 Minutes
Date / Time Fraction Description WHERE (date) > sysdate - 6/24; Past 6 hours. (or 1/4 of a day ago) WHERE (date) > sysdate - 6; Past six days WHERE (date) > sysdate - 6/1440; Past six minutes 6/24
1/46 hours 1/24/60/60
5/24/60/60One second
Five seconds1/24/60
5/24/60One minute
Five minutes1/24
5/24One hour
Five hoursTRUNC(SYSDATE+1/24,'HH') Every one hour starting with the next hour
Run Statspack Snapshot Every 1 Hourvariable jobno number;
variable instno number;
BEGIN
SELECT instance_number INTO :instno FROM v$instance;
DBMS_JOB.SUBMIT(:jobno, 'statspack.snap;', TRUNC(sysdate)+(TRUNC(to_char(sysdate,'sssss')/900)+1)*15/24/60, 'TRUNC(sysdate)+(TRUNC(to_char(sysdate,''sssss'')/900)+1)*15/24/60', TRUE, :instno);
COMMIT;
END;
/
DBMS_JOB / Every 15 Minutes from Monday to Friday, Between 6 a.m. and 6 p.m.variable jobno number;
variable instno number;
BEGIN
SELECT instance_number INTO :instno FROM v$instance;
DBMS_JOB.SUBMIT(:jobno, 'statspack.snap;', TRUNC(sysdate+1/24,'HH'), 'TRUNC(SYSDATE+1/24,''HH'')', TRUE, :instno);
COMMIT;
END;
/
If the database is Oracle8i (Release 2) or higher, you can simply use the CASE statement. The following CASE statement returns the correct interval for the above specification:SQL> ALTER SESSION SET nls_date_format = '(DY) MON DD, YYYY HH24:MI';The CASE statement gives you great flexibility in generating a complex value such as you need. Unfortunately, DBMS_JOB will allow you to use only an interval that is 200 characters or less, and even if you "scrunch up" the CASE statement above, you'll find it is about 300 characters minimally. So, you cannot use it directly in the call to DBMS_JOB. My solution to that is one of two things: either I would create a view NEXT_DATE as that select, so that select * from next_date would return the next time the job runs, or I would wrap the above query in a PL/SQL function that returns a date. If I used a view, my call to DBMS_JOB might look like:
Session altered.
SQL> SELECT
sysdate
, CASE
WHEN ( TO_CHAR(SYSDATE, 'HH24') BETWEEN 6 AND 17
AND
TO_CHAR(SYSDATE, 'DY') NOT IN ('SAT','SUN')
)
THEN TRUNC(sysdate) +
(TRUNC(TO_CHAR(sysdate,'sssss')/900)+1)*15/24/60
WHEN (TO_CHAR(sysdate, 'DY') NOT IN ('FRI','SAT','SUN'))
THEN TRUNC(sysdate)+1+6/24
ELSE next_day(trunc(sysdate), 'Mon') + 6/24
END interval_date
FROM dual;
SYSDATE
------------------------------
INTERVAL_DATE
------------------------------
sun sep 15, 2002 16:35
mon sep 16, 2002 06:00beginOr, if I used the PL/SQL function approach and created a function NEXT_DATE, it could be:
dbms_job.submit
( :n, 'proc;', sysdate,
'(select * from next_date)'
);
end;
/begin
dbms_job.submit
( :n, 'proc;', sysdate,
'next_date()'
);
end;
/