OracleのPL/SQLバッチ運用でジョブ管理を検討するとき、選択肢は実質DBMS_SCHEDULER一択です。旧DBMS_JOBはcronライクな最小機能しか持たない一方、DBMS_SCHEDULERは依存関係の制御・イベント駆動実行・リトライ制御・リソース管理・通知といったエンタープライズ運用に必須の仕組みを最初から備えています。
ただし基本的なCREATE_JOBだけを使って「cron代わり」で回している現場も多く、DBMS_SCHEDULERの真価であるジョブチェーン(CHAIN)による依存制御や、ファイル到着/キューで発火するイベント駆動ジョブを活用しきれていないケースが目立ちます。
この記事では定期実行の基本から、複数ジョブの依存関係をCHAINで制御する設計、イベント駆動ジョブの実装、失敗時の自動リトライ、ジョブクラスによるリソース制御、Email通知、運用オペレーション一覧、そして本番で頻発するトラブルの診断手順まで、実務で本当に効く知識を2026年版で整理します。
この記事でわかること
- DBMS_SCHEDULERの4大オブジェクト(JOB / PROGRAM / SCHEDULE / CHAIN)の役割分担
- REPEAT_INTERVAL実戦パターン(営業日のみ・月末・第2火曜など)
- ジョブチェーンで複数ジョブの依存関係を制御する設計
- キュー・ファイル到着・DBイベントで発火するイベント駆動ジョブ
- MAX_FAILURES・RESTARTABLEでリトライを自動化する方法
- ジョブクラス・リソースプランでSLAを守る設計
- Email通知(成功・失敗・超過)を公式機能で設定する手順
- 実行ログの追跡(DBA_SCHEDULER_JOB_RUN_DETAILS・JOB_LOG)
- 運用オペレーション(RUN_JOB・STOP_JOB・DISABLE・DROP)の使い分け
- 本番頻出トラブル(BLOCKED・STUCK・CHAIN停止)の診断と復旧
- 30秒でわかるDBMS_SCHEDULER運用の結論
- DBMS_SCHEDULERの4大オブジェクト|役割分担を理解する
- REPEAT_INTERVAL|業務要件を1行で表現するパターン集
- ジョブチェーン|依存関係を宣言的に制御する
- イベント駆動ジョブ|時刻ではなく「出来事」で発火させる
- 失敗時のリトライ設計|MAX_FAILURESとRESTARTABLE
- ジョブクラスで優先度とリソースを分離する
- Email通知|成功・失敗・超過を公式機能で送る
- 運用オペレーション一覧|RUN / STOP / DISABLE / DROPの使い分け
- 本番で踏むアンチパターン6選
- トラブルシューティング|本番頻出症状の診断手順
- よくある質問
- 関連記事で知識を深める
- まとめ|DBMS_SCHEDULERを運用の主役として活用する
30秒でわかるDBMS_SCHEDULER運用の結論
忙しい読者向けに「これだけ押さえれば運用が安定する」ポイントを先に提示します。
| 結論 | 理由・効果 |
|---|---|
| ① 複数ジョブの順序制御はCHAIN一択 | START_DATE時間差で繋ぐ設計は失敗時の整合性が壊れる |
② REPEAT_INTERVALはFREQ=...;BY...構文の組合せで表現 |
営業日・月末・第N曜日など複雑な業務要件も1行で記述可 |
| ③ 失敗時はMAX_FAILURES+RESTARTABLEで自動リトライ | 人手の再実行を減らして夜間放置OKの設計になる |
| ④ 重要ジョブはジョブクラスで優先度とリソースを分離 | 重いETLが軽い監視ジョブを押しつぶす事故を防ぐ |
| ⑤ 通知は自前実装ではなく公式のEmail通知機能を使う | SMTPサーバ1つ設定すれば成功・失敗・超過を自動通知 |
| ⑥ 実行履歴はPURGE_LOGで定期削除 | SYSAUX肥大化→ORA-01654(索引表領域不足)の定番事故を回避 |
| ⑦ DROPする前に必ずSTOP_JOBで実行中を停止 | 実行中のDROPはロック競合とトランザクション破損の温床 |
DBMS_SCHEDULERの4大オブジェクト|役割分担を理解する
DBMS_SCHEDULERは「JOBだけ作れば動く」と思われがちですが、実は4つのオブジェクトを組み合わせて使えるように設計されています。用途別に使い分けると再利用性と保守性が劇的に向上します。
JOB(ジョブ)|実行単位
実際に「いつ何を実行するか」を定義する最小単位です。一番シンプルな形では「PL/SQLブロック+スケジュール+有効フラグ」をCREATE_JOBで1回宣言すれば動き始めます。PROGRAMとSCHEDULEを参照する形で作ることもでき、その場合は処理内容と実行時刻を完全に分離管理できます。
PROGRAM(プログラム)|処理内容の部品化
「何を実行するか」を再利用可能な形で切り出すオブジェクトです。CREATE_PROGRAMで登録しておくと、複数のJOBから同じPROGRAMを参照できます。引数(DEFINE_PROGRAM_ARGUMENT)も定義できるので、「月次集計プログラムに対象年月を渡して呼び出す」のようなパラメータ化された処理に最適です。PROGRAMは手動実行や開発環境テスト時に独立して呼べるのが強みです。
SCHEDULE(スケジュール)|実行タイミングの部品化
「いつ実行するか」を再利用可能な形で切り出します。CREATE_SCHEDULEで「営業日9時」「月末深夜2時」などを意味のある名前で登録し、複数のJOBから参照できます。祝日カレンダーを反映した営業日スケジュールを一度作れば、全ジョブが同じカレンダーに追従するので保守コストが激減します。
CHAIN(チェーン)|依存関係の表現
複数のJOBやPROGRAMを「順序付きワークフロー」として繋ぐ仕組みです。STEP(実行単位)とRULE(遷移条件)で「A成功→B実行、A失敗→リカバリC実行」のような依存制御を宣言的に書けます。単なる時間差ではなく「前段の結果」でフロー分岐できるのがCHAIN最大の価値で、ETLパイプラインやリリースバッチの標準実装として不可欠です。
初心者が陥る罠:とりあえず全部JOBで作ってしまい、処理内容・スケジュール・依存関係が密結合してメンテ困難になるパターン。「業務ジョブが20本を超えたら」PROGRAMとSCHEDULEを分離する合図です。CHAINは3本以上の依存があるワークフローで検討開始します。
REPEAT_INTERVAL|業務要件を1行で表現するパターン集
DBMS_SCHEDULERのスケジュール式はカレンダー構文と呼ばれ、FREQ(頻度)を軸にBY系修飾子を組み合わせます。cron記法と違って業務日や第N曜日も素直に書けるのが強みです。
| 業務要件 | REPEAT_INTERVAL |
|---|---|
| 毎日深夜2時 | FREQ=DAILY;BYHOUR=2;BYMINUTE=0 |
| 平日のみ朝9時 | FREQ=WEEKLY;BYDAY=MON,TUE,WED,THU,FRI;BYHOUR=9;BYMINUTE=0 |
| 毎月1日の朝6時 | FREQ=MONTHLY;BYMONTHDAY=1;BYHOUR=6 |
| 毎月最終営業日の深夜 | FREQ=MONTHLY;BYMONTHDAY=-1;BYHOUR=23 ※祝日考慮なら祝日カレンダー併用 |
| 毎月第2火曜日の朝8時 | FREQ=MONTHLY;BYDAY=2TUE;BYHOUR=8 |
| 15分ごと | FREQ=MINUTELY;INTERVAL=15 |
| 毎四半期の初日 | FREQ=YEARLY;BYMONTH=1,4,7,10;BYMONTHDAY=1 |
| クリスマスに年1回 | FREQ=YEARLY;BYMONTH=12;BYMONTHDAY=25;BYHOUR=0 |
-- 1. 祝日を除外した「営業日のみ」の名前付きスケジュール
BEGIN
DBMS_SCHEDULER.CREATE_SCHEDULE(
schedule_name => 'BUSINESS_DAY_0900',
start_date => SYSTIMESTAMP,
repeat_interval =>
'FREQ=DAILY;BYDAY=MON,TUE,WED,THU,FRI;BYHOUR=9;BYMINUTE=0;' ||
'EXCLUDE=JP_HOLIDAYS', -- 事前にJP_HOLIDAYSカレンダーを登録しておく
comments => '平日9時(日本の祝日を除く)'
);
END;
/
-- 2. 複数ジョブから同じスケジュールを参照
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'DAILY_SYNC',
job_type => 'STORED_PROCEDURE',
job_action => 'PKG_SYNC.RUN_DAILY',
schedule_name => 'BUSINESS_DAY_0900', -- 共有スケジュールを参照
enabled => TRUE
);
END;
/
ベストプラクティス:祝日カレンダーはDBMS_SCHEDULER.CREATE_CALENDARで名前付き登録し、REPEAT_INTERVALのEXCLUDE=から参照します。祝日が変わっても「カレンダーだけ更新」すれば全ジョブに波及するため年末の祝日法改正対応が1箇所で済みます。
ジョブチェーン|依存関係を宣言的に制御する
ETLバッチやリリースバッチでは「抽出 → 変換 → ロード → 集計 → 通知」のような複数ジョブの連携が必要です。これを「START_DATEを30分ずつずらす」方式で組むと前段が長引いた瞬間に整合性崩壊しますが、DBMS_SCHEDULERのCHAINなら前段の結果に応じた遷移を宣言的に定義できます。
CHAINの骨格|STEPとRULEで組み立てる
CHAINは3つのAPIで構築します。① CREATE_CHAINでチェーン本体、② DEFINE_CHAIN_STEPで各ステップ(実行したいPROGRAM)、③ DEFINE_CHAIN_RULEでステップ間の遷移条件。ルールは「STEP1 SUCCEEDED」「STEP1 FAILED」「NOT STARTED」のような状態式で書きます。
-- ステップ1: チェーン本体の作成
BEGIN
DBMS_SCHEDULER.CREATE_CHAIN(
chain_name => 'DAILY_ETL_CHAIN',
comments => '日次ETL:抽出→変換→ロード/失敗時はリカバリ通知'
);
END;
/
-- ステップ2: 各ステップを定義(事前にPROGRAMを作成しておく)
BEGIN
DBMS_SCHEDULER.DEFINE_CHAIN_STEP('DAILY_ETL_CHAIN','STEP_EXTRACT', 'PRG_EXTRACT');
DBMS_SCHEDULER.DEFINE_CHAIN_STEP('DAILY_ETL_CHAIN','STEP_TRANSFORM','PRG_TRANSFORM');
DBMS_SCHEDULER.DEFINE_CHAIN_STEP('DAILY_ETL_CHAIN','STEP_LOAD', 'PRG_LOAD');
DBMS_SCHEDULER.DEFINE_CHAIN_STEP('DAILY_ETL_CHAIN','STEP_RECOVER', 'PRG_NOTIFY_ERROR');
END;
/
-- ステップ3: 遷移ルールの定義
BEGIN
-- 開始ルール:最初にEXTRACTを実行
DBMS_SCHEDULER.DEFINE_CHAIN_RULE(
'DAILY_ETL_CHAIN',
condition => 'TRUE',
action => 'START STEP_EXTRACT'
);
-- EXTRACT成功 → TRANSFORM
DBMS_SCHEDULER.DEFINE_CHAIN_RULE(
'DAILY_ETL_CHAIN',
condition => 'STEP_EXTRACT SUCCEEDED',
action => 'START STEP_TRANSFORM'
);
-- TRANSFORM成功 → LOAD
DBMS_SCHEDULER.DEFINE_CHAIN_RULE(
'DAILY_ETL_CHAIN',
condition => 'STEP_TRANSFORM SUCCEEDED',
action => 'START STEP_LOAD'
);
-- どこかで失敗 → リカバリ通知
DBMS_SCHEDULER.DEFINE_CHAIN_RULE(
'DAILY_ETL_CHAIN',
condition => 'STEP_EXTRACT FAILED OR STEP_TRANSFORM FAILED OR STEP_LOAD FAILED',
action => 'START STEP_RECOVER'
);
-- 正常終了条件:LOADまで成功 または RECOVERで通知完了
DBMS_SCHEDULER.DEFINE_CHAIN_RULE(
'DAILY_ETL_CHAIN',
condition => 'STEP_LOAD SUCCEEDED OR STEP_RECOVER COMPLETED',
action => 'END'
);
END;
/
-- ステップ4: チェーンを有効化&起動ジョブを作成
BEGIN
DBMS_SCHEDULER.ENABLE('DAILY_ETL_CHAIN');
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'RUN_DAILY_ETL',
job_type => 'CHAIN',
job_action => 'DAILY_ETL_CHAIN',
schedule_name => 'BUSINESS_DAY_0900',
enabled => TRUE
);
END;
/
CHAIN最大のメリット:①前段が長引いても自動で待ち(時間差待ちは不要)、②途中で失敗したら自動で別経路へ分岐、③進行状況がDBA_SCHEDULER_RUNNING_CHAINSで可視化される、④再実行はRUN_CHAINで「失敗ステップから」再開できる、の4点。複雑なシェルスクリプトでの連携は全てCHAINに置き換えられます。
イベント駆動ジョブ|時刻ではなく「出来事」で発火させる
DBMS_SCHEDULERは時刻ベースだけでなく、キューへのメッセージ到着やDBイベントでジョブを発火させることもできます。cronでは絶対に実現できない「外部トリガーで即応」をOracle内部で完結できるのが大きな魅力です。
キュー駆動ジョブ|AQ(Advanced Queuing)と連携
event_conditionでキューメッセージの属性を判定し、該当するメッセージが入ってきた瞬間にジョブが起動します。外部システムからAPIでメッセージを投げれば「ファイル到着→即ETL」が実現できます。
-- 1. イベントキューを作成
BEGIN
DBMS_AQADM.CREATE_QUEUE_TABLE(
queue_table => 'FILE_ARRIVAL_QT',
queue_payload_type => 'SYS.SCHEDULER_FILEWATCHER_RESULT');
DBMS_AQADM.CREATE_QUEUE(
queue_name => 'FILE_ARRIVAL_Q',
queue_table => 'FILE_ARRIVAL_QT');
DBMS_AQADM.START_QUEUE('FILE_ARRIVAL_Q');
END;
/
-- 2. イベント発火条件でジョブを作成
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'FILE_ARRIVAL_JOB',
job_type => 'STORED_PROCEDURE',
job_action => 'PKG_LOAD.RUN_FOR_FILE',
event_condition => q'[tab.user_data.file_name LIKE 'sales_%.csv']',
queue_spec => 'FILE_ARRIVAL_Q',
enabled => TRUE,
comments => 'sales_*.csv到着で即起動'
);
END;
/
ファイル到着検知(FILE_WATCHER)
外部ディレクトリのファイル到着を直接ポーリングさせる仕組みも用意されています。CREATE_FILE_WATCHERで監視対象ディレクトリとパターンを定義し、そのウォッチャーをイベントソースとするジョブを組めば、5秒〜数分間隔で新規ファイルを検知して処理を走らせられます。外部バッチサーバでcronを回して「ファイル到着を監視してsqlplusを叩く」構成をOracle内部に閉じ込められるので運用が格段にシンプルになります。
-- クレデンシャル作成(OSユーザで監視する)
BEGIN
DBMS_CREDENTIAL.CREATE_CREDENTIAL(
credential_name => 'ORACLE_OS_CRED',
username => 'oracle',
password => '<password>'
);
END;
/
-- FILE_WATCHERの作成
BEGIN
DBMS_SCHEDULER.CREATE_FILE_WATCHER(
file_watcher_name => 'INBOX_WATCHER',
directory_path => '/data/inbox',
file_name => '*.csv',
credential_name => 'ORACLE_OS_CRED',
destination => NULL, -- ローカル監視
enabled => TRUE
);
END;
/
-- FILE_WATCHERイベントをソースとするジョブ
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'INBOX_LOAD_JOB',
program_name => 'PRG_LOAD_INBOX', -- 事前に登録したPROGRAM
event_condition => q'[tab.user_data.file_name LIKE '%.csv']',
queue_spec => 'INBOX_WATCHER, SYS.SCHEDULER_FILEWATCHER_Q',
auto_drop => FALSE,
enabled => TRUE
);
END;
/
失敗時のリトライ設計|MAX_FAILURESとRESTARTABLE
バッチジョブは「失敗したら自動で再試行」が運用の定石です。DBMS_SCHEDULERはSET_ATTRIBUTEで再試行ポリシーを属性として設定でき、連続失敗数の上限・再試行の有効化・再試行間隔を柔軟にコントロールできます。
RESTARTABLE|自動リトライを有効化
デフォルトはFALSEで、失敗してもリトライしません。TRUEに設定すると、実行中のエラーで失敗した場合に自動で再試行を試みます。「ネットワーク瞬断」「一時的ロック競合」「外部API 5xx」など再実行で回復する可能性がある障害に有効です。
MAX_FAILURES|連続失敗上限でジョブを自動停止
指定回数連続で失敗したら、ジョブをBROKEN状態にして以降の自動起動を停止します。「失敗し続けてDB負荷を圧迫する」事故を防ぐガードとして必須です。典型値は3〜5。この回数を超えたら人が調査すべき根本障害とみなすラインです。
MAX_RUN_DURATION|長時間化ジョブの強制停止
「本来10分で終わるETLが4時間走り続ける」ような異常長時間化を防ぐ仕組みです。指定時間を超えたら自動でSTOP_JOBが呼ばれ、MAX_FAILURESのカウントにも計上されます。
BEGIN
-- 既存ジョブにリトライ属性を設定
DBMS_SCHEDULER.SET_ATTRIBUTE('DAILY_SYNC', 'RESTARTABLE', TRUE);
DBMS_SCHEDULER.SET_ATTRIBUTE('DAILY_SYNC', 'MAX_FAILURES', 3);
DBMS_SCHEDULER.SET_ATTRIBUTE('DAILY_SYNC', 'MAX_RUNS', NULL);
DBMS_SCHEDULER.SET_ATTRIBUTE('DAILY_SYNC', 'MAX_RUN_DURATION',
INTERVAL '30' MINUTE); -- 30分で強制停止
DBMS_SCHEDULER.SET_ATTRIBUTE('DAILY_SYNC', 'RAISE_EVENTS',
DBMS_SCHEDULER.JOB_FAILED + DBMS_SCHEDULER.JOB_BROKEN
+ DBMS_SCHEDULER.JOB_RUN_COMPLETED); -- 主要イベントで通知可
END;
/
-- BROKENから復旧する(原因調査後)
BEGIN
DBMS_SCHEDULER.ENABLE('DAILY_SYNC'); -- 失敗カウンタもリセット
END;
/
ジョブクラスで優先度とリソースを分離する
全ジョブが同じキュー・同じリソースで動くと、「重いETLが走っている間、軽い監視ジョブまで遅延する」事故が起きます。ジョブクラス(JOB CLASS)を使えば、ジョブをグループ化してリソースプランと紐づけ、SLAクラス別の実行分離が実現できます。
-- 重要ジョブ用クラス(リソース高優先度)
BEGIN
DBMS_SCHEDULER.CREATE_JOB_CLASS(
job_class_name => 'CRITICAL_JC',
resource_consumer_group => 'CRITICAL_GROUP', -- リソースマネージャと連携
logging_level => DBMS_SCHEDULER.LOGGING_FULL,
log_history => 365, -- 1年分の履歴を保持
comments => '基幹バッチ用:高優先度'
);
DBMS_SCHEDULER.CREATE_JOB_CLASS(
job_class_name => 'LOW_JC',
logging_level => DBMS_SCHEDULER.LOGGING_RUNS,
log_history => 30,
comments => '軽量監視用:低優先度'
);
END;
/
-- ジョブにクラスを割り当て
BEGIN
DBMS_SCHEDULER.SET_ATTRIBUTE('DAILY_ETL_CHAIN_JOB', 'JOB_CLASS', 'CRITICAL_JC');
DBMS_SCHEDULER.SET_ATTRIBUTE('HEARTBEAT_JOB', 'JOB_CLASS', 'LOW_JC');
END;
/
Email通知|成功・失敗・超過を公式機能で送る
「失敗したら関係者にメール」は自前でUTL_SMTPを書いていた時代もありましたが、DBMS_SCHEDULERには公式のEmail通知機能が組み込まれています。SMTPサーバを登録してADD_JOB_EMAIL_NOTIFICATIONを呼ぶだけで設定完了です。
-- 1. SMTPサーバを登録(1回だけ)
BEGIN
DBMS_SCHEDULER.SET_SCHEDULER_ATTRIBUTE('email_server', 'smtp.example.com:25');
DBMS_SCHEDULER.SET_SCHEDULER_ATTRIBUTE('email_sender', 'alert@example.com');
END;
/
-- 2. 特定ジョブに通知を追加
BEGIN
DBMS_SCHEDULER.ADD_JOB_EMAIL_NOTIFICATION(
job_name => 'DAILY_ETL_CHAIN_JOB',
recipients => 'ops@example.com, oncall@example.com',
sender => 'alert@example.com',
subject => 'ETL通知 [{jobname}] - {event_type}',
body => 'Job: {jobname}%N状態: {event_type}%Nエラー: {error_message}',
events => 'JOB_FAILED, JOB_BROKEN, JOB_OVER_MAX_DUR'
);
END;
/
-- 通知を削除する場合
-- DBMS_SCHEDULER.REMOVE_JOB_EMAIL_NOTIFICATION('DAILY_ETL_CHAIN_JOB');
イベント種類はJOB_FAILED(失敗)・JOB_BROKEN(連続失敗で停止)・JOB_OVER_MAX_DUR(長時間化)・JOB_SUCCEEDED(成功)など。成功通知は頻度が多いので本番では普通オフにし、失敗・ブロークン・超過の3点だけ通知する設計が標準です。
運用オペレーション一覧|RUN / STOP / DISABLE / DROPの使い分け
日々の運用で頻繁に使うコマンドをまとめます。停止(STOP)と無効化(DISABLE)と削除(DROP)は別物なので混同しないのが肝です。
| 操作 | コマンド | 用途 |
|---|---|---|
| 手動実行(即時) | DBMS_SCHEDULER.RUN_JOB('X') |
動作確認・再実行 |
| 実行中ジョブを停止 | DBMS_SCHEDULER.STOP_JOB('X', force=>TRUE) |
長時間化・暴走時の緊急停止 |
| 自動起動を停止(定義は残す) | DBMS_SCHEDULER.DISABLE('X') |
メンテ期間中に一時停止 |
| 自動起動を再開 | DBMS_SCHEDULER.ENABLE('X') |
メンテ復旧時・BROKEN復旧時 |
| ジョブ削除 | DBMS_SCHEDULER.DROP_JOB('X', force=>TRUE) |
不要ジョブの完全削除 |
| 実行履歴の削除 | DBMS_SCHEDULER.PURGE_LOG(log_history=>30) |
SYSAUX肥大化防止(30日以前を削除) |
| 属性変更 | DBMS_SCHEDULER.SET_ATTRIBUTE('X','ATTR',VAL) |
スケジュール・通知先の変更 |
DROPの順序に注意:実行中のジョブをいきなりDROPするとトランザクションが中途半端に中断されロールバックで長時間ロックが発生することがあります。本番では必ずSTOP_JOB → DISABLE → DROPの順で段階的に停止してください。force=TRUEも可能ですが、「本当に止まったか」をDBA_SCHEDULER_RUNNING_JOBSで確認してから次段階へ進むのが鉄則です。
本番で踏むアンチパターン6選
① START_DATEを時間差にして依存を表現する
「A:2時開始・B:3時開始」で順序を作る設計は、Aが伸びた瞬間に整合性が壊れます。依存関係はCHAIN、または「B内の先頭でAの完了をチェック」方式に置き換えてください。
② PURGE_LOGを回さずSYSAUXを肥大化
DBMS_SCHEDULERの実行履歴はSYSAUX表領域に書かれます。数年放置するとGB単位で膨れ、SYSAUX拡張不能→ORA-01688で基幹機能停止、という事故が実在します。月1回のPURGE_LOGジョブを必ず組んでください。
③ BROKEN状態を放置
連続失敗でBROKENになったジョブは以降一切自動実行されません。監視でBROKENを検知しない運用だと「気付いたら1週間日次バッチが止まっていた」という悲劇に直結します。Email通知のJOB_BROKENを必ず有効化してください。
④ job_actionにPL/SQLをベタ書きして長大化
CREATE_JOBのjob_actionに100行のPL/SQLを書き込むと改修のたびにジョブ再作成が必要になり、変更履歴もDBA_SCHEDULER_JOBS上で追いにくい。処理はパッケージ化し、job_actionにはPKG_X.DOだけ書くのが鉄則です。
⑤ 同じ処理を複数ジョブにコピペ
PROGRAM・SCHEDULE・CALENDAR機能を使えば「処理定義1本・スケジュール定義1本」で複数のジョブを作れます。コピペ設計は祝日法改正や処理修正時に全ジョブを書き換える羽目になります。
⑥ 通知を自前UTL_SMTPで書く
ADD_JOB_EMAIL_NOTIFICATIONを使えば失敗・超過・BROKENの主要イベントが即通知されます。自前実装は「通知処理自体がエラーで止まる」二次障害を生むので、公式機能を優先してください。
トラブルシューティング|本番頻出症状の診断手順
「予定時刻になっても動かない」「実行中のまま止まった」「CHAINが途中停止」など、現場で頻発する症状と診断コマンドをまとめます。
| 症状 | 診断クエリ/対処 |
|---|---|
| ジョブが起動しない | SELECT state, enabled FROM dba_scheduler_jobs WHERE job_name='X';DISABLED/BROKENなら ENABLE・SCHEDULED確認 |
| 実行履歴で失敗 | SELECT * FROM dba_scheduler_job_run_details WHERE job_name='X' ORDER BY log_date DESC; |
| 実行中なのに進まない | DBA_SCHEDULER_RUNNING_JOBSのSESSION_IDからV$SESSIONを突合してロック・イベント調査 |
| CHAINが途中停止 | DBA_SCHEDULER_RUNNING_CHAINSで停滞ステップを確認、DBMS_SCHEDULER.RUN_CHAINで再開 |
| BROKEN解除したい | DBMS_SCHEDULER.ENABLE('X');で失敗カウンタごとリセット |
| SYSAUXが肥大化 | SELECT * FROM dba_scheduler_job_log WHERE log_date<SYSDATE-30;件数確認→PURGE_LOG |
| Email通知が届かない | SELECT * FROM dba_scheduler_notifications;+SMTP設定属性確認+スパム判定チェック |
よくある質問
DBMS_SCHEDULER.MIGRATE_DBMS_JOBSを使って一括移行できます。DBMS_SCHEDULER.EVALUATE_CALENDAR_STRINGで「そのREPEAT_INTERVALは次にいつ動くか」をN回分シミュレーションできます。本番登録前に必ず数件の予定時刻を確認することを推奨します。ジョブを即試したいときはRUN_JOB(use_current_session => FALSE)でバックグラウンド実行すれば、スケジュールに関係なく即時確認できます。PKG_X.DOの内部で失敗カウンタを管理しDBMS_SCHEDULER.CREATE_JOBで遅延起動ジョブを投入する設計に切り替えます。DBMS_SCHEDULER.ALTER_RUNNING_CHAINで特定ステップをRUN/SKIP/STOPに切り替えて再開できます。障害発生時は「失敗ステップだけ修復→その後SKIPで飛ばして進める」「別の経路からRUNで再実行」といった運用が可能です。DBMS_CREDENTIALでOSユーザ+パスワードを登録し、ジョブのCREDENTIAL_NAME属性に指定すればそのOSユーザで外部コマンド(job_type => 'EXECUTABLE')を実行できます。セキュリティ観点では「Oracle管理者権限を持たない専用ユーザ」を作って最小権限で動かすのが鉄則です。CREATE_JOB_DESTINATIONでリモートDBを登録しておけば、ジョブ自体をリモートDBで実行する「分散ジョブ」も組めます。ただし分散ジョブは障害時の切り分けが複雑になるので、まずはDBリンクによる呼び出しで運用するのが無難です。REPEAT_INTERVALにFREQ=MINUTELY;INTERVAL=30と書くとデフォルトは「開始時刻から30分ごと」ですが、DBMS_SCHEDULER.SET_ATTRIBUTE('X','schedule_type','PLSQL')のように「前回終了時刻を基準にする」スタイルに切り替える実装も可能です。シンプルな要件なら、ジョブの最後で次回実行時刻を計算してCREATE_JOBで再投入する自己繰り返しパターンが確実です。log_history属性)。ジョブクラス単位で個別に設定でき、365日まで指定できます。保持期間を超えた履歴はPURGE_LOGまたは自動パージで削除されます。長期保存が必要な場合はカスタムログテーブルに定期コピーする(6545の集中ロギング設計を参照)のが実務的です。DBA_SCHEDULER_RUNNING_JOBSでジョブ名ごとのセッションIDが取得できます。V$SESSIONと結合すれば待機イベント・SQL IDまで追跡可能で、遅いジョブのリアルタイム診断に威力を発揮します。ALTER SYSTEM KILL SESSIONでのセッション強制終了も必要ならここから行います。DBMS_METADATA.GET_DDL('PROCOBJ', 'JOB_NAME')でJOB・PROGRAM・SCHEDULE・CHAINすべてをDDL文字列として取得できます。これをgit管理のSQLスクリプトに保存しておけば「本番のジョブ定義をリポジトリに戻す」サイクルが回せます。バージョン管理されたジョブ定義は改修時の差分レビューもしやすく、実務で非常に有効です。関連記事で知識を深める
DBMS_SCHEDULER周りをさらに深掘りするなら次の記事もどうぞ。
- 【Oracle】DBMS_SCHEDULER完全ガイド|ジョブ作成・スケジュール管理・実行履歴確認・エラー対処まで解説(基本構文・CREATE_JOB・REPEAT_INTERVAL基礎)
- 【PL/SQL】ジョブ実行履歴の集中管理と異常検知(DBMS_SCHEDULER+ロギング)(独自ログテーブル設計・SLA監視)
- 【Oracle】ジョブの確認方法完全ガイド|DBA_SCHEDULER_JOBS・DBA_JOBS・実行履歴・トラブルシューティング(運用時のジョブ確認クエリ集)
- 【Oracle】DBMS_JOB完全ガイド|ジョブの作成・変更・停止・DBMS_SCHEDULER との違いまで解説(レガシー環境の移行時に)
- 【PL/SQL】MERGE文でUPSERTを高速・安全に実装(ジョブで回すETLのデータ統合部分)
- 【PL/SQL】パイプライン関数で大量データ処理を勝たせる完全ガイド(定期ジョブで回す大量変換処理)
- 【Oracle】自律型トランザクション(AUTONOMOUS_TRANSACTION)完全ガイド(ジョブ内のログ書き込みで活用)
- 【PL/SQL】例外処理の書き方と使い方(ジョブ失敗時のハンドリング設計)
- 【Oracle】Resource Manager完全ガイド(ジョブクラスと連携するリソース制御)
- 【PL/SQL】ストアドプロシージャとファンクションの違いと作り方(ジョブから呼び出すPROGRAM本体の設計)
まとめ|DBMS_SCHEDULERを運用の主役として活用する
DBMS_SCHEDULERは「ちょっと高機能なcron」ではなく、Oracle内部で完結する本格的なジョブオーケストレーターです。CREATE_JOBだけで止まらず、PROGRAM/SCHEDULE/CHAINの4オブジェクトを使い分けることで、エンタープライズの複雑な要件も宣言的に表現できます。本記事の押さえどころを7点に絞って再掲します。
- 処理・スケジュール・依存はPROGRAM/SCHEDULE/CHAINに分離して再利用する
- REPEAT_INTERVALのカレンダー構文で業務要件を1行で表現する
- 複数ジョブの順序制御はSTART_DATE時間差ではなくCHAINで宣言的に書く
- イベント駆動(キュー・FILE_WATCHER)で「時刻に縛られない」ジョブを作る
- MAX_FAILURES+RESTARTABLE+MAX_RUN_DURATIONでリトライと暴走防止を自動化
- Email通知は公式機能(ADD_JOB_EMAIL_NOTIFICATION)を使い自前SMTPは書かない
- PURGE_LOGを組み込みSYSAUX肥大化を防止、BROKEN検知の通知を必ず有効化
既存のcron+シェルスクリプト運用をDBMS_SCHEDULERへ移行するだけで、「失敗通知が来なかった」「前段が伸びて後段と衝突した」といった典型トラブルが構造的に根絶できます。本記事のコード例をベースに、自分の環境で1本ずつCHAINに置き換えていってください。

