어느 Salesforce Developer의 개발 성장기

Salesforce 신입 개발자가 가장 많이 하는 실수(System.LimitException: Too many SOQL queries / Too many DML statements) 본문

Salesforce/Tips

Salesforce 신입 개발자가 가장 많이 하는 실수(System.LimitException: Too many SOQL queries / Too many DML statements)

Developer_Foryou 2020. 3. 11. 00:10

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;
}

 

Comments