Apexバッチのscopeの決まり方と同時実行数の上限
件数が多いデータを一括更新すると、同期処理の上限にすぐ当たります。Apexバッチはレコードを自動で分割し、非同期のトランザクションで処理します。
Apexバッチを使う場面
取引先を1万件まとめて更新するような処理を、同期のApexでそのまま書くとSOQLやDMLの回数、実行時間の上限にすぐ当たります。Apexバッチは対象レコードを自動的に決まった件数ずつへ分割し、それぞれを別のトランザクションとして非同期に実行します。分割と非同期実行を、自分でコントローラを書かずに任せられるのが利点です。
Database.Batchableの3つのメソッド
Database.Batchableインターフェースを実装すると、3つのメソッドを書く決まりになります。
| メソッド | 呼ばれるタイミング | 役割 |
|---|---|---|
start | ジョブ開始時に1回 | 処理対象のレコード集合を返します |
execute | 分割されたバッチごとに複数回 | 渡されたレコードの一部(scope)を処理します |
finish | 全バッチの処理後に1回 | 後処理やメール通知などを行います |
startはSOQLの結果をそのまま返すDatabase.QueryLocatorと、任意の集合を返せるIterable<sObject>のどちらかを返せます。SOQLで対象を絞れるならQueryLocatorで十分です。
Account_Batchable.cls
public with sharing class Account_Batchable implements Database.Batchable<sObject> { public Database.QueryLocator start(Database.BatchableContext bc) { return Database.getQueryLocator('SELECT Id, Name FROM Account'); } public void execute(Database.BatchableContext bc, List<Account> scope) { for (Account acc : scope) { acc.Name = acc.Name + 'BatchTest'; } update scope; } public void finish(Database.BatchableContext bc) { System.debug('バッチ処理が終了しました'); } }
取引先を検索してscope件ずつ名前を書き換え、1回のupdateでまとめて保存します。ループの中にDMLはありません。
scopeの既定値と上限
Database.executeBatchを呼ぶときに、scope引数で1回のexecuteに渡す件数を指定できます。
| 指定 | 件数 |
|---|---|
| 省略した場合 | 200件ずつに分割されます |
startがQueryLocatorを返す場合の上限 | 2,000件まで指定できます |
⚠️ 最後のバッチだけは、scopeで指定した件数より少ないことがあります。対象が1,000件でscopeを300にすると、最後のexecuteには100件だけが渡ります。executeの中で件数が常にscopeと同じだとは仮定しないでください。
バッチをスケジュールで定期実行する
Schedulableインターフェースを実装したクラスから、Database.executeBatchを呼び出す形で定期実行できます。
Account_Schedule.cls
public class Account_Schedule implements Schedulable { private final Integer BATCH_SIZE = 200; public void execute(SchedulableContext sc) { Account_Batchable b = new Account_Batchable(); Database.executeBatch(b, BATCH_SIZE); } }
スケジューラからバッチを起動します。BATCH_SIZEがexecuteBatchのscope引数になります。
登録は開発者コンソールからSystem.scheduleを呼ぶか、設定画面の「Apexをスケジュール」から行います。
String jobId = System.schedule('Account_Schedule_Daily', '0 0 2 * * ?', new Account_Schedule());
毎日午前2時に実行するcron式の例です。過去の日時を指定すると、スケジュール登録の時点でエラーになります。
開発者コンソールで動かして確認する
Execute Anonymous Windowを開く
開発者コンソールのメニューから「Debug」→「Open Execute Anonymous Window」を選びます。
バッチを実行するコードを書いて実行する
Account_Batchable b = new Account_Batchable(); Database.executeBatch(b, 200);を入力し、「Execute」を押します。ジョブの状態を確認する
設定の「Apexジョブ」を開き、対象のジョブが「完了」になっていることを確認します。取引先の一覧を開き、名前に「BatchTest」が付いていることも確認します。
スケジュールの登録を確認する
System.scheduleを実行した場合は、設定の「スケジュール済みジョブ」に登録されていることを確認します。
バッチの同時実行数と1日の上限
| 項目 | 上限 |
|---|---|
| 組織全体で同時にキュー投入・実行できるバッチジョブ数 | 5個まで(超えた分はApex Flexキューで「保留中」として待機します) |
| Apex Flexキューで待機できるバッチジョブ数 | 100個まで |
| 24時間あたりのバッチApexメソッド実行数(start・execute・finishの合計) | 250,000回、または組織のユーザーライセンス数×200のいずれか大きい方まで |
24時間の上限は、バッチApexだけでなく他の非同期Apex(キューアブルなど)とも共有します。バッチを細かく分けすぎると、executeの呼び出し回数がかさんでこの上限に近づきます。
トランザクションをまたいで値を持ち越す
バッチはexecuteが呼ばれるたびに別のトランザクションになるため、インスタンス変数は毎回リセットされます。処理件数の合計を数えるなど、バッチ全体を通して値を持ち越したいときはDatabase.Statefulを付けます。
public with sharing class Account_Batchable implements Database.Batchable<sObject>, Database.Stateful { public Integer processedCount = 0; public Database.QueryLocator start(Database.BatchableContext bc) { return Database.getQueryLocator('SELECT Id, Name FROM Account'); } public void execute(Database.BatchableContext bc, List<Account> scope) { for (Account acc : scope) { acc.Name = acc.Name + 'BatchTest'; } update scope; processedCount += scope.size(); } public void finish(Database.BatchableContext bc) { System.debug('処理件数: ' + processedCount); } }
Database.Statefulを付けると、processedCountがexecuteをまたいでも保持されます。付けないと毎回0からになります。
Database.Statefulで保持されるのはインスタンス変数だけです。静的変数はDatabase.Statefulを付けても、トランザクションごとにリセットされます。
大量件数を扱うときの選択肢
バッチApexは1日あたりの実行回数に上限があるため、数百万件を超えるような初期移行やデータクレンジングには向きません。そうした件数は、Apexを介さずに直接データを送り込める形式のBulk APIや、それを内部で使うデータローダを使います。バッチサイズの目安として、公式ガイドラインは1万件からの開始を勧めています。処理時間を見ながら増減させます。
ここで間違えやすい
| 間違い | 何が起きるか |
|---|---|
scopeに2,000を超える値を指定する | QueryLocator利用時は、指定しても2,000件ずつに分割されます。指定した件数では渡りません |
Database.Statefulを付けずに合計を数える | executeのたびにインスタンス変数が0に戻り、正しく集計できません |
| 過去の日時でcron式を組む | System.scheduleの時点でエラーになり、ジョブが登録されません |
| バッチを次々にキューへ積む | 同時にキュー投入・実行できるのは5個までです。超えた分はApex Flexキューで待機し、Flexキューの100個も超えるとLimitExceptionになります |
確認した環境
- 2026年9月 / Salesforce Summer '26(APIバージョン67.0)時点の公式ドキュメントで、
Database.Batchableの仕様、scopeの既定値と上限、同時実行数と24時間あたりの実行回数の上限を確認しています
まとめ
- Apexバッチは
start・execute・finishの3メソッドを実装します。対象の分割と非同期実行はSalesforceが行います scopeは省略すると200件、QueryLocator使用時の上限は2,000件です。最後のバッチはそれより少ないことがあります- 組織全体で同時に動かせるバッチジョブは5個まで、24時間の実行回数上限は250,000回か「ライセンス数×200」の大きい方です
- トランザクションをまたいで値を保持したいときは
Database.Statefulを付けます - 数百万件規模の処理は、バッチApexではなくBulk APIやデータローダを検討します
Salesforceの導入・運用についてご相談ください
導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。