Salesforceの DML(データ操作言語) には制限があり、適切に処理しないと ガバナ制限違反 でエラーが発生します。
今回は、DMLの制限と、それを考慮したベストプラクティスとアンチパターン を具体的なコードとともに整理しました。
DMLの主な制限
-
実行されるDMLステートメントの合計数
-
1回のトランザクションで150回まで の
INSERT
,UPDATE
,DELETE
,UPSERT
,MERGE
,UNDELETE
が実行可能。
-
1回のトランザクションで150回まで の
-
DMLステートメントの結果として処理されるレコードの合計数
- 1回のトランザクションで10,000件まで のレコードを処理可能。
-
ヒープメモリの合計サイズ
- 6MB(同期処理)または12MB(非同期処理)まで。
- メモリを使いすぎると
System.LimitException: Apex heap size too large
エラーが発生。
1. ❌ アンチパターン: DMLステートメントを個別実行
✅ 理由
- DML制限(最大150回)にすぐ到達 してしまう。
- 処理速度が遅くなる(DMLは1回ごとにデータベースアクセスが発生するため)。
📌 ❌ 悪い例(DMLステートメントを個別実行)
public void updateContacts() {
List<Contact> contacts = [SELECT Id, LastName FROM Contact];
for (Contact con : contacts) {
con.LastName = 'Updated Name';
update con; // ❌ ループ内でDMLを実行(150件超えるとエラー)
}
}
⛔ 問題点
- コンタクトが150件以上あるとエラー(DML制限違反)
- データベースへのアクセス回数が多く、パフォーマンスが低下
✅ 良い例(バッチ処理を使用)
public void updateContactsBatch() {
List<Contact> contacts = [SELECT Id, LastName FROM Contact];
for (Contact con : contacts) {
con.LastName = 'Updated Name';
}
update contacts; // ✅ 1回のDMLでまとめて更新
}
✅ DMLを1回にまとめることで、制限回避&高速化!
2. ✅ 原則:DMLステートメントのためのバッチデータ
✅ 理由
- ループ内のDMLを削減し、ガバナ制限を回避 する。
- データをコレクションに集め、DMLを一括で実行することで高速化。
📌 ❌ 悪い例(ループ内でDMLを実行)
public void insertAccountsIndividually() {
for (Integer i = 0; i < 200; i++) {
Account acc = new Account(Name = 'Test Account ' + i);
insert acc; // ❌ ループごとにDMLが発生(150件超えるとエラー)
}
}
⛔ 問題点
-
150回以上のDML実行でエラー (
System.LimitException: Too many DML statements
) - データベースへの負荷が大きく、処理速度が低下
📌 ✅ 良い例(リストを使ってDMLをバッチ処理)
public void insertAccountsBatch() {
List<Account> accountsToInsert = new List<Account>();
for (Integer i = 0; i < 200; i++) {
accountsToInsert.add(new Account(Name = 'Test Account ' + i));
}
insert accountsToInsert; // ✅ 1回のDMLで一括処理
}
✅ データをリストにまとめ、DMLを1回で実行することで、パフォーマンス向上&ガバナ制限回避!
3. ✅ 原則:SOQL for ループを使用して200件ごとのバッチを作成
✅ 理由
- ヒープサイズ制限を回避する(データを少量ずつ処理)。
- SOQLの結果を自動的にバッチ処理できる。
📌 ❌ 悪い例(全レコードを一度に取得)
List<Account> accounts = [SELECT Id, Name FROM Account];
for (Account acc : accounts) {
System.debug(acc.Name);
}
⛔ 問題点
- 取得したデータが多すぎると、ヒープサイズ制限(6MB)に達する可能性あり。
📌 ✅ 良い例(SOQL for ループで200件ずつ処理)
for (Account acc : [SELECT Id, Name FROM Account]) {
System.debug(acc.Name); // ✅ 少量ずつ処理し、メモリ使用量を抑える
}
✅ SOQL for ループを使うと、Salesforceが自動的にレコードをバッチ処理し、ヒープサイズを抑える!
📌 ✅ 大量データを安全に更新するバッチ処理
List<Account> accountsToUpdate = new List<Account>();
for (Account acc : [SELECT Id, Name FROM Account]) {
acc.Name = acc.Name + ' Updated';
accountsToUpdate.add(acc);
if (accountsToUpdate.size() == 200) {
update accountsToUpdate; // ✅ 200件ごとにDML実行
accountsToUpdate.clear();
}
}
// 残りのレコードを処理
if (!accountsToUpdate.isEmpty()) {
update accountsToUpdate;
}
✅ 200件ごとに処理することで、ガバナ制限を回避&パフォーマンス向上!
🎯 まとめ
項目 | ❌ 悪い例 | ✅ 良い例 |
---|---|---|
DMLをループ内で実行 |
update con; をループ内で実行 |
リストにまとめて1回のDMLで処理 |
DMLのバッチ処理 |
insert acc; を200回実行 |
200件ごとにDMLを実行 |
SOQL for ループを活用 |
List<Account> accounts = [SOQL] で全取得 |
for (record : [SOQL]) を使用 |