function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
マスターキートン!マスターキートン! 

複数のオブジェクトにまだがる大量一括更新について

大量レコードを扱うにあたり、apex batchを実装しています。

その際、Aというオブジェクトから取得したデータをBに突っ込むといったケースの実装につきまして、

まずはじめに、startメソッドでAのデータを取得したのち
executeメソッドにて、さらにAのキーを含むBのデータを取得して、
AのデータとBのデータをforで回しながら、
キーが一致したらアップデートするというような処理を行うとした場合、
Bのデータがexecuteでデータを取得しているので、ガバナ制限に引っかかるといったことは
あるでしょうか。

上記が実装できない場合は、以下のように実装しようかと考えています。

Aのデータを取得して、finishメソッドで別のバッチを起動し、その際、AのscopeをBに渡す。


```
   global List<sObject> scopeA;

   global void execute(Database.BatchableContext BC, List<sObject> scope) {
        scopeA = scope;
   } 
    global void finish(Database.BatchableContext BC) {
        BatchB batchB = new BatchB(scopeA);
        Database.ExecuteBatch(batchB);
    }
```

以下にて、ロケータでAを取得しAを更新するのは理解していますが、
http://tyoshikawa1106.hatenablog.com/entry/2015/02/15/182505
Aを取得した結果をBにいれる場合について
最適な方法を教えて頂ければ幸いです。









 
Best Answer chosen by マスターキートン!
Taiki YoshikawaTaiki Yoshikawa
forループ内でUpdate処理を実行するとガバナ制限に引っかかります。

更新用のリストを1つ用意して、forループ → 条件にマッチ → 更新用リストに追加 → ループ後にUpdate処理を実行という流れが望ましいと思います。

また、『finishメソッドで別のバッチを起動し、その際、AのscopeをBに渡す。』という方法ですが、Apexバッチでは繰り返し実行されるのはexecute処理のみとなっています。finishは最後に1回のみなので上記書き方の場合だとscopeAが毎回上書きされてしまうことになります。

例) バッチ処理の流れ
  1. 処理件数が1000件でバッチ実行
  2. 200件ずつ(指定したバッチサイズずつ)に分割されてexecute処理を実行
  3. さいごにfinish処理を実行

少し話がそれますが、finish処理で別のバッチ処理を呼び出すということ自体は問題ありません。

All Answers

Taiki YoshikawaTaiki Yoshikawa
forループ内でUpdate処理を実行するとガバナ制限に引っかかります。

更新用のリストを1つ用意して、forループ → 条件にマッチ → 更新用リストに追加 → ループ後にUpdate処理を実行という流れが望ましいと思います。

また、『finishメソッドで別のバッチを起動し、その際、AのscopeをBに渡す。』という方法ですが、Apexバッチでは繰り返し実行されるのはexecute処理のみとなっています。finishは最後に1回のみなので上記書き方の場合だとscopeAが毎回上書きされてしまうことになります。

例) バッチ処理の流れ
  1. 処理件数が1000件でバッチ実行
  2. 200件ずつ(指定したバッチサイズずつ)に分割されてexecute処理を実行
  3. さいごにfinish処理を実行

少し話がそれますが、finish処理で別のバッチ処理を呼び出すということ自体は問題ありません。
This was selected as the best answer
マスターキートン!マスターキートン!
ご回答ありがとうございます。
勉強になります。


>更新用のリストを1つ用意して、forループ → 条件にマッチ → 更新用リストに追加 → ループ後にUpdate処理を実行という流れが望ましいと思います。

はい、その認識はあります。


現在、以下のような実装をしてみました。

更新するものをまず、queryLocatorで取得しておいて(executeメソッドのscope変数)
executeメソッドでbデータを取得して、
キーが一致したらscopeを内容を書き換え
ループの外側の最後でupdateする形です。

パフォーマンスは悪そうですが、
それ以外にも、ガバナ制限にもかかりそうな気もしてはいます。

最適な方法あれば教えて頂ければ幸いです。
global void execute(Database.BatchableContext BC, List<sObject> scope) {
 
       Set<Id> Ids = (new Map<Id,SObject>(scope)).keySet();

        // scope(objA)を1件単位に
		for(objA a : scope) {
            // objBを取得(以下の書き方だと200ずつで分割で取得できるよう)
			for (List<objB> bList : [
			SELECT
			  ID
             , Name
			FROM
			  objB
			Where
			  Id = :Ids
			]) {
                // 1件単位に
				for (objB b : bList) {
					if (a.Id == bList.Id ) {
                          // 更新処理
					}
				}
			}
		}

		update scope;

}