Salesforce 신입 개발자가 가장 많이 하는 실수(System.LimitException: Too many SOQL queries / Too many DML statements)
Salesforce는 동기 Apex에 대해 100개의 SOQL 쿼리 또는 비동기 Apex에 대해 200개의 SOQL 쿼리 제한이 있다.
즉, 반복문(for문) 안에서 쿼리문을 사용하면 안된다.
Performing Bulk SOQL
SOQL 검색시, 잘못된 쿼리 예시(1):
trigger SoqlTriggerNotBulk on Account(after update) {
for(Account a : Trigger.New) {
// Get child records for each account
// Inefficient SOQL query as it runs once for each account!
Opportunity[] opps = [SELECT Id,Name,CloseDate
FROM Opportunity WHERE AccountId=:a.Id];
// Do some other processing
}
}
위 코드는 Trigger가 발동되는 레코드가 많을 때, System.LimitException을 발생시킬 수 있다.
SOQL 쿼리 제한을 피하기 위해 다음과 같이 코드를 작성해야 한다.
SOQL 검색시, 올바른 쿼리 예시(1):
trigger SoqlTriggerBulk on Account(after update) {
// Perform SOQL query once.
// Get the accounts and their related opportunities.
List<Account> acctsWithOpps =
[SELECT Id,(SELECT Id,Name,CloseDate FROM Opportunities)
FROM Account WHERE Id IN :Trigger.New];
// Iterate over the returned accounts
for(Account a : acctsWithOpps) {
Opportunity[] relatedOpps = a.Opportunities;
// Do some other processing
}
}
SOQL 검색시, 올바른 쿼리 예시(2): 계정의 레코드가 필요하지 않은 경우 이 Trigger Context 내의 계정과 관련된 기회만 검색할 수 있다.
trigger SoqlTriggerBulk on Account(after update) {
// Perform SOQL query once.
// Get the related opportunities for the accounts in this trigger.
List<Opportunity> relatedOpps = [SELECT Id,Name,CloseDate FROM Opportunity
WHERE AccountId IN :Trigger.New];
// Iterate over the related opportunities
for(Opportunity opp : relatedOpps) {
// Do some other processing
}
}
SOQL 검색시, 올바른 쿼리 예시(3): 하나의 명령문에서 for루프를 사용하여 SOQL 쿼리를 결합하면 이전 예제의 크기를 줄일 수 있다.
trigger SoqlTriggerBulk on Account(after update) {
// Perform SOQL query once.
// Get the related opportunities for the accounts in this trigger,
// and iterate over those records.
for(Opportunity opp : [SELECT Id,Name,CloseDate FROM Opportunity
WHERE AccountId IN :Trigger.New]) {
// Do some other processing
}
}
Bulk SOQL이 진행된 경우, 트리거는 한 번에 200 레코드씩 일괄적으로 실행한다. 따라서 400개의 레코드가 트리거를 발생시키면 트리거는 200개의 레코드 당 1번씩 2번 트리거된다.
Performing Bulk DML
Trigger나 Class에서 DML호출을 수행할 때는 가능하면 sObject Collection에서 DML호출을 수행하자.
각 sObject에서 DML을 수행하면 비효율적으로 리소스가 개별적으로 사용된다.
(Apex Runtime은 하나의 transaction에서 150개의 DML호출을 허용한다.
DML 실행 시, 잘못된 쿼리 예시(1):
trigger DmlTriggerNotBulk on Account(after update) {
// Get the related opportunities for the accounts in this trigger.
List<Opportunity> relatedOpps = [SELECT Id,Name,Probability FROM Opportunity
WHERE AccountId IN :Trigger.New];
// Iterate over the related opportunities
for(Opportunity opp : relatedOpps) {
// Update the description when probability is greater
// than 50% but less than 100%
if ((opp.Probability >= 50) && (opp.Probability < 100)) {
opp.Description = 'New description for opportunity.';
// Update once for each opportunity -- not efficient!
update opp;
}
}
}
DML 실행 시, 올바른 쿼리 예시(1): List에 레코드를 담아서 한번에 Update
trigger DmlTriggerBulk on Account(after update) {
// Get the related opportunities for the accounts in this trigger.
List<Opportunity> relatedOpps = [SELECT Id,Name,Probability FROM Opportunity
WHERE AccountId IN :Trigger.New];
List<Opportunity> oppsToUpdate = new List<Opportunity>();
// Iterate over the related opportunities
for(Opportunity opp : relatedOpps) {
// Update the description when probability is greater
// than 50% but less than 100%
if ((opp.Probability >= 50) && (opp.Probability < 100)) {
opp.Description = 'New description for opportunity.';
oppsToUpdate.add(opp);
}
}
// Perform DML on a collection
update oppsToUpdate;
}