3

I am trying to find duplicate accounts in the system and trying to merge them based on our filter criteria using apex. The issue is I am hitting the governor limit of 150 DML statements while doing this.

We will have to put the merge statement inside the loop as we need the parent and child as the parameters in the merge statement. I don't think we can bulkify Merge like other DML statements due to the above reason. I am not sure if there is any better way to do it and avoid hitting governor limits.

Any help in this regard would be greatly appreciated.

Code that I have tried is included below

global class LoyaltyAccountBatch implements Database.batchable<sObject>{ 


    global Database.QueryLocator start(Database.batchableContext info){ 

        String query;
        query = ('SELECT  Id FROM  LoginHistory LIMIT 1');

        return Database.getQueryLocator(query);
   }  

   global void execute(Database.batchableContext info, List<LoginHistory> scope){ 



        // Get those records based on the IDs
        List<Account> templst1 = [SELECT Id,UniqID from account where createddate = LAST_N_DAYS:4 and UniqID <> '']; 


         system.debug('templist is '+templst1);    


        Set<String> insertedRecords = new Set<String>() ;   

    for (Account a : templst1)  {
        insertedRecords.add(a.UniqID);
    }

               List<Account> templst = [SELECT Id,UniqID from account where NewUniqID IN : insertedRecords 
                                 order by NewUniqID,UniqID asc];

       // Process records 
        for(integer i=0;i+1<templst.size();i++) 
        {
            if(templst[i].UniqID <  templst[i+1].UniqID)        
            {
                system.debug('ids are '+templst[i].UniqID+'-->'+templst[i+1].UniqID);
                String str = '1'+templst[i].UniqID.substring(1,templst[i].UniqID.length());
                String str1 = '1'+templst[i+1].UniqID.substring(1,templst[i+1].UniqID.length());
                system.debug('str is'+str);
                system.debug('str1 is'+str1);
                if(str.equals(str1))
                {
                    Merge templst[i+1] templst[i];
                    system.debug('value of i is'+i);
                } 
            }
        }   

   }     

   global void finish(Database.batchableContext info){     
   } 

}
10
  • So...can you share what you have tried? It should be no problem in a batch where you can control the batch size.
    – Adrian Larson
    Commented Oct 18, 2016 at 3:04
  • OK, I must be missing something then as I have written a batch. I have edited the question to include my code. Please check it now.
    – user27749
    Commented Oct 18, 2016 at 3:14
  • That doesn't really look like de-duplication...you never check if the two values are equal. Don't you just want to merge records that have the same UniqueId__c?
    – Adrian Larson
    Commented Oct 18, 2016 at 3:19
  • 1
    @AdrianLarson Ah. Right. That's not the purpose of the start method...
    – sfdcfox
    Commented Oct 18, 2016 at 3:21
  • 1
    Yes, I get your point regarding the start method.I will try it. Thanks heaps @AdrianLarson.
    – user27749
    Commented Oct 18, 2016 at 3:33

1 Answer 1

2

You certainly are missing the point of the batch. Query the records you want to merge in your start method, not your execute method. By doing so, you then control how many records you pass to each execute block. Your de-duplication strategy is somewhat confusing, but if you just want to merge records that have the same UniqueId__c value, I recommend batching over AggregateResult here.

public List<AggregateResult> start(...)
{
    return Database.getQueryLocator([
        SELECT UniqueId__c FROM Account
        GROUP BY UniqueId__c
        HAVING count(Id) > 1
    ]);
}
public void execute(...)
{
    Map<String, List<Account>> accounts = new Map<String, List<Account>>();
    for (AggregateResult aggregate : scope)
        accounts.put((String)aggregate.get('UniqueId__c'));
    for (Account record : [SELECT Name FROM Account WHERE Name IN :accounts.keySet()])
        accounts.get(record.Name).add(record);

    for (List<Account> duplicates : accounts.values())
        merge duplicates[0] duplicates[1];
}
public void finish(...)
{
}

If you pursue a strategy similar to the above, you will be able to limit the scope size at execution time to actually control how many records you try to de-duplicate in one go.

Database.executeBatch(new MyBatch(), /*scope_size*/ Limits.getLimitDmlStatements());

Obviously the execute code could use some refinement, such as error handling and considerations if there are more than two records with the same name. The above should help get you started, though.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .