SalesforceでApexを実行したとき、次のエラーが表示されることがあります。
System.LimitException: Too many SOQL queries: 101このエラーは、1つのトランザクション内で実行したSOQLクエリの回数が上限を超えたときに発生します。ループ内のSOQLが代表的な原因ですが、実際には複数のApexクラスやトリガーで使った回数が積み上がり、最後に実行したSOQLで上限を超えることもあります。
そのため、エラーが表示された行だけを見て直すのではなく、同じトランザクションでSOQLがどこから積み上がったかを確認することが重要です。
先に結論です。Too many SOQL queries: 101が発生したら、まずデバッグログのLIMIT_USAGE_FOR_NSで使用回数を確認します。続いてLimits.getQueries()、SOQL_EXECUTE_BEGIN、スタックトレースを追い、複数のApex処理のどこで使用回数が増えたかを切り分けます。LIMIT 1や取得項目の削減だけでは、SOQLの発行回数は減らないため直接的な解決にはなりません。
Too many SOQL queries: 101の確認ポイント
最初に、原因と確認場所を一覧にすると次のようになります。
| 主な原因 | 確認する場所 | 対処の方向 |
|---|---|---|
| ループ内でSOQLを実行している | Apexコード、SOQL_EXECUTE_BEGIN | IDをSetに集め、ループ外で一括取得する |
| 少数レコードだけを前提にしている | 一括登録・更新時のログ、テストクラス | トリガーとクラスを複数レコード対応にする |
| Apexトリガーが再帰している | スタックトレース、同じトリガーの実行回数 | 起動条件と更新設計を見直す |
| Flowがループ内でレコードを取得している | Flowの要素、デバッグログ | Get Recordsをループ外へ移す |
| ApexとFlowなど複数の自動化が動いている | 実行順序、ログ全体 | 同一オブジェクトの自動化を横断して確認する |
| 非同期処理を含む設計になっている | Queueable、Batch、Scheduled Apex | トランザクション境界と処理量を確認する |
ボンボンApexにSOQLが数個しかなくても、Flowや別のトリガーが先に使っていることがあるよ。エラー行だけじゃなく、ログ全体を見てみよう。
SOQL 101が発生するガバナ制限を確認する
これはSOQLの構文エラーではなく、Salesforceのガバナ制限に関するエラーです。Salesforceはマルチテナント環境のリソースを保護するため、1つのApexトランザクションで使用できるSOQLやDMLなどに上限を設けています。
Salesforce公式のExecution Governors and Limitsでは、同期Apexと非同期ApexのSOQL上限を次のように定めています。
| 実行コンテキスト | SOQLクエリの上限 | 例 |
|---|---|---|
| 同期処理 | 100回 | 画面操作、同期Apex、通常のApexトリガー |
| 非同期処理 | 200回 | Queueable Apex、Futureメソッドなど |
上限は「取得したレコードの件数」ではなく、基本的にはSOQLを実行した回数です。1回のSOQLで100件取得する場合と、1件ずつ100回SOQLを実行する場合では、消費するクエリ回数が異なります。
非同期処理へ移せば必ず解決するわけではありません。上限が増えて一時的にエラーが消えても、ループ内SOQLや再帰が残っていれば、データ量の増加によって再発します。まずSOQLの発行回数を減らす設計を確認します。
Too many SOQL queries: 101を再現する
今回の検証は2026年7月に、Salesforceの検証用組織と開発者コンソールを使って行いました。
同じ挙動を確認する場合は、SandboxまたはDeveloper Editionなどの検証用組織で、開発者コンソールの「Execute Anonymous Window」から次のコードを実行します。
for (Integer i = 0; i < 101; i++) {
List<Account> accounts = [
SELECT Id
FROM Account
LIMIT 1
];
}同期Apexでは100回までSOQLを実行できますが、101回目のクエリでSystem.LimitExceptionが発生します。ループ回数を100へ変更した場合と比較すると、境界を確認できます。
| ループ回数 | SOQL実行回数 | 実行結果 |
|---|---|---|
| 99回 | 99回 | 成功 |
| 100回 | 100回 | 成功 |
| 101回 | 101回 | Too many SOQL queries: 101 |


LIMIT 1を付けても101エラーになる理由
再現コードではLIMIT 1を付けていますが、取得件数を1件にしてもSOQL自体はループのたびに実行されます。そのため、101回目でエラーになります。
取得項目を減らす、WHERE条件を追加する、インデックスを利用するといった対応は、取得量やクエリ性能の改善には有効です。ただし、同じ回数のSOQLを実行している限り、Too many SOQL queriesの直接的な解決にはなりません。


LimitsクラスでSOQLの使用回数を確認する
Apexコードの途中でSOQLの使用回数を確認したい場合は、Limitsクラスを利用できます。
System.debug(
LoggingLevel.ERROR,
'SOQL Queries: ' +
Limits.getQueries() +
' / ' +
Limits.getLimitQueries()
);Limits.getQueries()は現在のトランザクションで実行済みのSOQL回数、Limits.getLimitQueries()はそのコンテキストで実行できる上限を返します。詳細はSalesforce公式のLimitsクラスで確認できます。
既存処理のどこでクエリ数が増えているか分からない場合は、処理の節目へ一時的にログを追加し、メソッドの前後で数値を比較します。
System.debug('before: ' + Limits.getQueries());
executeBusinessLogic();
System.debug('after: ' + Limits.getQueries());Limitsクラスは調査に便利ですが、上限そのものを増やす機能ではありません。原因箇所を絞り込んだら、SOQLの回数を減らす実装修正が必要です。


デバッグログから複数箇所の積み上がりを特定する
ここまでは、1つの処理でSOQLを101回実行する単純な例を確認しました。実際の開発では、複数のApexクラスやメソッドが同じトランザクション内で動き、少しずつ使用回数を積み上げていることがあります。
そこで、取引先処理で40回、取引先責任者処理で35回、最後の商談処理で26回SOQLを実行する検証用コードを作ります。エラーが出るのは商談処理ですが、その時点ですでに75回消費している状態です。
| 処理 | SOQL実行回数 | 処理後の累計 |
|---|---|---|
| Soql101AccountService | 40回 | 40回 |
| Soql101ContactService | 35回 | 75回 |
| Soql101MultiScenarioの商談処理 | 26回 | 101回 |


以下は検証用コードです。SOQLを意図的に繰り返すため、本番組織や本番データでは実行せず、SandboxまたはDeveloper Editionを使用してください。
1.複数のApex処理を用意する
最初に、SOQLを40回実行する取引先処理を作成します。
public with sharing class Soql101AccountService {
public static void consume() {
System.debug(
LoggingLevel.ERROR,
'AccountService開始: ' + Limits.getQueries()
);
for (Integer i = 1; i <= 40; i++) {
List<Account> accounts = [
SELECT Id FROM Account LIMIT 1
];
}
System.debug(
LoggingLevel.ERROR,
'AccountService終了: ' + Limits.getQueries()
);
}
}続いて、SOQLを35回実行する取引先責任者処理を作成します。
public with sharing class Soql101ContactService {
public static void consume() {
System.debug(
LoggingLevel.ERROR,
'ContactService開始: ' + Limits.getQueries()
);
for (Integer i = 1; i <= 35; i++) {
List<Contact> contacts = [
SELECT Id FROM Contact LIMIT 1
];
}
System.debug(
LoggingLevel.ERROR,
'ContactService終了: ' + Limits.getQueries()
);
}
}最後に2つの処理を呼び出し、商談のSOQLを実行するクラスを作成します。商談処理は、100回で止める場合と101回目まで進める場合を比較できるようにしています。
public with sharing class Soql101MultiScenario {
public static void run(Integer opportunityQueryCount) {
System.debug(
LoggingLevel.ERROR,
'処理開始: ' + Limits.getQueries()
);
Soql101AccountService.consume();
Soql101ContactService.consume();
System.debug(
LoggingLevel.ERROR,
'商談処理開始前: ' + Limits.getQueries()
);
for (Integer i = 1; i <= opportunityQueryCount; i++) {
if (i == 1 || i == 25 || i == 26) {
System.debug(
LoggingLevel.ERROR,
'商談クエリ実行前 ' + i + '回目: ' +
Limits.getQueries()
);
}
List<Opportunity> opportunities = [
SELECT Id FROM Opportunity LIMIT 1
];
}
}
}

2.100回と101回の実行結果を比較する
まず、Execute Anonymous Windowから次のコードを実行します。
Soql101MultiScenario.run(25);40回+35回+25回で合計100回となり、今回の検証では正常終了しました。続いて、商談処理を1回増やします。
Soql101MultiScenario.run(26);今度は合計101回となり、今回の検証では最後の商談SOQLでSystem.LimitException: Too many SOQL queries: 101が発生しました。


3.ログから40回、75回、100回の節目を確認する
対象ユーザーへ追跡フラグを設定してからrun(26)を再実行し、デバッグログを開きます。今回追加した出力を検索すると、SOQLの使用回数が次のように増えていることを確認できます。
処理開始: 0
AccountService開始: 0
AccountService終了: 40
ContactService開始: 40
ContactService終了: 75
商談処理開始前: 75
商談クエリ実行前 25回目: 99
商談クエリ実行前 26回目: 100今回の検証では、エラーが表示されたのは商談処理でした。一方、実際にログを確認すると、その前の2クラスですでに75回使っていたことが分かりました。最後のSOQLだけを修正対象と考えるのではなく、取引先処理と取引先責任者処理も含めて見直す必要があります。
ポイント:例外の行は「101回目を実行しようとした場所」です。SOQLを積み上げた原因箇所が、同じクラスや同じ行にあるとは限りません。


4.LIMIT_USAGE_FOR_NSとSOQL_EXECUTE_BEGINを確認する
ログ内でLIMIT_USAGE_FOR_NSを検索すると、SOQLやDMLなどの使用状況を確認できます。Salesforce公式のデバッグログの説明にも、SOQLクエリ数が出力されることが記載されています。
LIMIT_USAGE_FOR_NS|(default)|
Number of SOQL queries: 100 out of 100上限到達を確認したら、SOQL_EXECUTE_BEGINとスタックトレースから、どのクラスと行番号でSOQLが繰り返されたかを確認します。今回なら、エラー直前の商談処理だけでなく、ログ上部のSoql101AccountServiceとSoql101ContactServiceまで確認対象です。
SalesforceのWell-Architectedでも、ガバナ制限を超えた処理が根本原因とは限らず、再帰的・重複した自動化を含めて確認する必要があると説明されています。


原因1:ループ内でSOQLを実行している
最も分かりやすい原因は、forループの中でSOQLを実行しているケースです。
修正前:レコードごとにSOQLを実行する
for (Contact contactRecord : Trigger.new) {
Account accountRecord = [
SELECT Id, Name
FROM Account
WHERE Id = :contactRecord.AccountId
];
// accountRecordを使った処理
}このコードは、対象の取引先責任者が1件ならSOQLも1回です。しかし、複数レコードを一括更新すると、レコード数に応じてSOQLの回数も増えます。
修正後:IDを集めてループ外で一括取得する
Set<Id> accountIds = new Set<Id>();
for (Contact contactRecord : Trigger.new) {
if (contactRecord.AccountId != null) {
accountIds.add(contactRecord.AccountId);
}
}
Map<Id, Account> accountMap = new Map<Id, Account>([
SELECT Id, Name
FROM Account
WHERE Id IN :accountIds
]);
for (Contact contactRecord : Trigger.new) {
Account accountRecord =
accountMap.get(contactRecord.AccountId);
// accountRecordを使った処理
}最初のループでは取引先IDだけをSetへ集め、SOQLはループ外で1回だけ実行します。取得結果をMapにすると、後続のループで取引先IDから該当レコードを参照できます。
Salesforce公式のRunning Apex within Governor Execution Limitsでも、ループ内のSOQLは反復ごとに実行され、100回の上限を超える可能性があるため避けるよう案内されています。
原因2:一括処理を想定できていない
通常の画面操作では1件ずつ更新されるため問題がなくても、データローダー、API連携、リストビューの一括更新などで複数件が同時に処理されると、急に101エラーが発生することがあります。
ApexトリガーではTrigger.newに複数レコードが入る前提で、SOQLとDMLを一括実行できるようにします。テストクラスでも1件だけでなく、複数レコードをまとめて登録・更新するテストを用意すると見つけやすくなります。
- メソッドの引数が単一IDだけを前提にしていないか
Trigger.newのループ内でSOQLやDMLを実行していないか- ヘルパークラスがレコードごとに呼び出されていないか
- テストクラスで複数レコードを一括処理しているか
原因3:Apexトリガーが再帰している
トリガーやFlowが対象レコードを更新し、その更新をきっかけに同じ自動化が再び起動すると、SOQLが繰り返し実行されます。
FlowとApexが同じレコードを更新したときの値の上書きと再実行は、次の関連記事で実際に検証しています。


再帰を防ぐときは、単に静的変数で2回目以降を止めるだけでなく、次の点を確認します。
- 更新前後で対象項目が変化したときだけ処理する
- 同じレコードの項目更新なら、可能な場合はbeforeトリガーや高速項目更新を使う
- 処理済みレコードIDを管理し、必要な再実行まで止めないようにする
- ApexとFlowの両方で同じ項目を更新していないか確認する
補足:実際の組織ではFlowや別トリガーも確認する
今回の検証では、原因を追いやすくするために3つのApexクラスだけを使用し、Flowは混ぜていません。
ただし、実際のレコード保存では、before-save Flow、Apexトリガー、after-save Flowなどが決められた順序で実行されます。詳しい順序はSalesforce公式のTriggers and Order of Executionで確認できます。
Apex側のSOQLが少なくても、前後で動くFlow、別オブジェクトのトリガー、サブフローなどを合計すると上限へ達する場合があります。Apexだけを修正しても使用回数が下がらない場合は、次の範囲まで確認します。
- 同じApexトリガーやクラスが繰り返し実行されていないか
- FlowのGet Recordsがループ内にないか
- FlowとApexが同じレコードを相互に更新していないか
- 関連レコードの更新で別オブジェクトの自動化が起動していないか
Flowの実行ユーザーやデバッグログの確認方法は、次の関連記事でも実際の画面を使って整理しています。


ApexコードにSOQLが見つからないときの確認順
エラーがApexから返っていても、確認しているクラスにSOQLが見つからないことがあります。その場合は、次の順番で範囲を広げます。
- スタックトレースから呼び出し元のクラスとトリガーを確認する
- 対象オブジェクトに設定されたレコードトリガーフローを確認する
- 同じレコードを再更新している処理がないか確認する
- 関連レコードの更新で別オブジェクトの自動化が起動していないか確認する
- インストール済みパッケージの処理がログに含まれていないか確認する
Limits.getQueries()を処理の節目へ追加し、増加箇所を絞り込む



「自分のクラスにSOQLがない=Apexは無関係」とは限らないよ。呼び出し先や後続処理まで、トランザクション単位で確認しよう。
効果がない、または注意が必要な対処
| 対処 | 101エラーへの効果 | 注意点 |
|---|---|---|
LIMIT 1を付ける | 直接的には解決しない | 取得件数は減るが、SOQLの実行回数は減らない |
| SELECT項目を減らす | 直接的には解決しない | ヒープや性能の改善には有効 |
| WHERE条件やインデックスを見直す | 直接的には解決しない | 選択性や実行時間の改善には有効 |
| 非同期処理へ移す | 状況による | 上限が200回へ増えても、設計上の問題が残る可能性がある |
| バッチサイズを下げる | 状況による | 処理単位は減るが、根本原因を残さないか確認する |
非同期化が要件に合う場合は、Salesforceが現在案内しているQueueable Apexなどを検討します。ただし、同期で完了すべき処理を上限回避だけのために非同期へ移すと、エラー処理や実行順序が複雑になります。
解決できないときのチェックリスト
- デバッグログの
LIMIT_USAGE_FOR_NSを確認したか SOQL_EXECUTE_BEGINが同じ場所で繰り返されていないか- ループ内にSOQLやDMLがないか
- 1件ではなく複数件を一括処理したときも動くか
- Apexトリガーが同じレコードを再更新していないか
- レコードトリガーフローやサブフローが起動していないか
- FlowとApexで同じ項目を相互に更新していないか
- 別オブジェクトの自動化まで連鎖していないか
Limits.getQueries()で増加箇所を比較したか- 非同期化やバッチサイズ変更で問題を隠していないか
まとめ
Too many SOQL queries: 101は、1つのトランザクションでSOQLの上限を超えたときに発生します。ループ内SOQLを修正するだけで解決するケースもありますが、Apexトリガーの再帰やFlowとの組み合わせが原因になることもあります。
- 最初に
LIMIT_USAGE_FOR_NSでSOQLの使用数を確認する SOQL_EXECUTE_BEGINとスタックトレースから繰り返し箇所を探す- IDを
Setへ集め、ループ外でSOQLを一括実行する - ApexだけでなくFlowや後続の自動化も確認する
LIMIT 1や取得項目の削減だけでは、SOQLの回数は減らない
エラーが出た行は、単に101回目のクエリを実行しようとした場所かもしれません。そこだけを直すのではなく、同じトランザクションで何が動いたかをログから順番に追うと、根本原因を見つけやすくなります。




コメント