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
sihmeieossihmeieos 

"System.LimitException: Too many SOQL queries: 21"について

こんにちは。

 

ApexTriggerとApexClassを使って処理をさせている箇所で、「System.LimitException: Too many SOQL queries: 21」が発生するようになってしまいました。以前は問題なく処理できていたので、何か新しい処理が走っていると思います。原因はわからないのですが、「System.LimitException: Too many SOQL queries: 21」の内容について確認したいと思います。

 

[Trigger]

 

trigger beforeUpdateProduct on Product__c (before update) {
    Product__c[] p = Trigger.new;
    /* Call ApexClass Method */
    CalculateDate.calcProductDate(p);
    /* Call ApexClass Method */
    CreateUpdateAuto.createRental(p);
}

 

上記のようなトリガー、つまり、Classをコールしているだけのトリガーだと、トリガーのガバナー制限(20回)にはひっかからず、Class内で100回SOQLを発行しなければ問題ないと思っていたのですが、違うのでしょうか。

テストだと正常に処理できて、画面からだとエラーになってしまうので、どこでガバナー制限にひっかかっているのか原因を特定できないのです。

テストだと、Classでは58回までSOQLをコールしているようなのですが、エラーにはなっていません。

認識に間違い、または原因に心当たりがある場合、ご指摘いただけるととても助かります。

よろしくお願いします。

 

Best Answer chosen by Admin (Salesforce Developers) 
ikouikou

どもです。

 

>私のガバナ制限に対する理解は上記のように考えており、これだとテストの時も画面からの操作の時も

特に変わらずに制限値にひっかからないはずです。

 

ここに誤解があります。

どこで実行されたかではなく、どこから呼ばれたか、です。

 

確認するためにSampleを作ってみました。

 

[Trigger]クラスをコールするだけ

trigger AccountTest on Account (after insert, after update) {
	System.debug('TriggerでのSOQL数リミット=' + Limits.getLimitQueries());
	new DemoAccountTriggerCall().method();
}

 

[Class]Triggerから呼ばれてクエリを21回投げる(テストメソッド付き)

public with sharing class DemoAccountTriggerCall {

	public void method(){
		System.debug('ClassでのSOQL数リミット=' + Limits.getLimitQueries());
		
		for(Integer i=0; i <= 20; i++){
			System.debug('@@@' + Limits.getQueries());
			Account acc = [SELECT Name FROM Account limit 1];
		}
	}

	public static testMethod void myUnitTest(){
		System.debug('testMethodでのSOQL数リミット=' + Limits.getLimitQueries());
		
		insert new Account(name='取引先名');
	}

}

 

上記Sampleを動かすために画面からAccountを作成すると、SOQLのLimitはTrigger、Class共に20になり、21回目のクエリを投げるところでエラーになります。

ただ、テストメソッドを動かすためにrunTestしてみると、testMethod内はもちろん、Trigger、Classともに100となり、21回目のクエリも無事に投げて正常終了します。

 

つまり、画面から(つまりTriggerから)処理が開始されれば、その中でClassを呼んでいようが最初のTriggerが終了するまで通して20という制限になり、

testMethodから処理が開始されれば、そこから呼ばれたTrigger、Class全てを通して100になるということです。

 

ポイントは、どこから処理が始まったかです。

All Answers

ikouikou

こんにちは。

 

> 上記のようなトリガー、つまり、Classをコールしているだけのトリガーだと、トリガーのガバナー制限(20回)にはひっかからず、Class内で100回SOQLを発行しなければ問題ないと思っていたのですが、違うのでしょうか。

 

Triggerから呼ばれている以上、Triggerのガバナー制限値が適用されます。

 

つまりSOQLクエリ総数の制限値は、Triggerであれば20回まで。

テストメソッドでは100回までとなります。

 

ちなみに、Limitsを使うと各処理においてのガバナー制限値が確認できます。

System.debug('@@@' + Limits.getLimitQueries());

 上記をTriggerから呼ばれたClass内で実行すると20、テストメソッドで実行すると100が返ってくるはずです。

sihmeieossihmeieos

ikouさんこんにちは。

いつもありがとうございます。

 

現在、テストではガバナ制限値(100)を超えず、エラーは発生しないのですが、画面からの操作だとガバナ制限値(20)を超えてエラーが発生してしまいます。

以下のような流れでCallしています。

 

■テストの場合

テストクラス

↓ insert オブジェクト

Triggerコール 【ここでのSOQLコール数が20以内】

↓ Triggerから処理用のClassコール

Classでの処理 【ここでのSOQLコール数が100以内】

 

■画面から実行の場合

データ作成

Triggerコール 【ここでのSOQLコール数が20位内】

↓ Triggerから処理用のClassコール

Classでの処理 【ここでのSOQLコール数が100位内】

 

私のガバナ制限に対する理解は上記のように考えており、これだとテストの時も画面からの操作の時も

特に変わらずに制限値にひっかからないはずです。

ですが、実際は、画面から実行の場合のみガバナ制限値(20)を超えたとのエラーが発生してしまいます。

何か違いがあるのでしょうか・・・。

 

ちなみに、教えていただいたLimitsオブジェクトを使用してみましたが、Trigger, TriggerからコースされているClass、

テストメソッド全てで100と表示されてしまいました。

 

ikouikou

どもです。

 

>私のガバナ制限に対する理解は上記のように考えており、これだとテストの時も画面からの操作の時も

特に変わらずに制限値にひっかからないはずです。

 

ここに誤解があります。

どこで実行されたかではなく、どこから呼ばれたか、です。

 

確認するためにSampleを作ってみました。

 

[Trigger]クラスをコールするだけ

trigger AccountTest on Account (after insert, after update) {
	System.debug('TriggerでのSOQL数リミット=' + Limits.getLimitQueries());
	new DemoAccountTriggerCall().method();
}

 

[Class]Triggerから呼ばれてクエリを21回投げる(テストメソッド付き)

public with sharing class DemoAccountTriggerCall {

	public void method(){
		System.debug('ClassでのSOQL数リミット=' + Limits.getLimitQueries());
		
		for(Integer i=0; i <= 20; i++){
			System.debug('@@@' + Limits.getQueries());
			Account acc = [SELECT Name FROM Account limit 1];
		}
	}

	public static testMethod void myUnitTest(){
		System.debug('testMethodでのSOQL数リミット=' + Limits.getLimitQueries());
		
		insert new Account(name='取引先名');
	}

}

 

上記Sampleを動かすために画面からAccountを作成すると、SOQLのLimitはTrigger、Class共に20になり、21回目のクエリを投げるところでエラーになります。

ただ、テストメソッドを動かすためにrunTestしてみると、testMethod内はもちろん、Trigger、Classともに100となり、21回目のクエリも無事に投げて正常終了します。

 

つまり、画面から(つまりTriggerから)処理が開始されれば、その中でClassを呼んでいようが最初のTriggerが終了するまで通して20という制限になり、

testMethodから処理が開始されれば、そこから呼ばれたTrigger、Class全てを通して100になるということです。

 

ポイントは、どこから処理が始まったかです。

This was selected as the best answer
sihmeieossihmeieos

ikouさん、こんにちは。

 

レスが遅くなってしまい、申し訳ありません。

とてもわかりやすく解説していただき、ありがとうございました。

 

どうも、私はガバナ制限について明確に理解できていないようです。

 

■Triggerからのコール(=UIからの実行):最初のトリガーが処理終了するまでSOQLは20回までコール可能

■Classからのコール(TestMethodからの実行含む):処理が終了するまでSOQLは100回までコール可能

 

とりあえず、上記の理解で良いと思っていますが、当初の「200回まで」との関連がまだ良くわかりません…。

あと、ガバナ制限について、Triggerから、Classからのコールで上限値が変わるといった記述がどこにあったかも

まだ調べられていません。

ガバナ制限はForce.comでの開発では気をつけなければいけない点なので、もう少しちゃんと調べてみます。

 

ありがとうございました。

 

 

sihmeieos