0

I am working on a batch class:

Code:

Global class deDuplicateRecords implements Database.Batchable<sObject>,Database.Stateful{
    global Set<String> ein;
    global Set<String> n;
    global Set<String>u;
    global Set<String>p;
    global Database.QueryLocator start(Database.BatchableContext bc)
    {
        return Database.getQueryLocator([Select Name,EIN__c,Website,Phone from Account WHERE EIN__c!=Null]);
    }
    global void execute(Database.BatchableContext bc,List<Account>acc){
        List<Account>accToBeDeleted=new List<Account>();
        Map<String,Account> accountMap=new Map<String,Account>();
        for(Account a:acc){        
            if(!Accountmap.containsKey(a.EIN__c)){
                accountMap.put(a.EIN__c,a);
            }else{
                Account existingAcc=accountMap.get(a.EIN__c);
                existingAcc.Website=a.Website;
                existingAcc.Phone=a.Phone;
                existingAcc.Name=a.Name;
                accountMap.put(a.EIN__c,existingAcc);
                accToBeDeleted.add(a);
            }
        }
        update accountMap.values() ;
        if(accToBeDeleted.size()>0){
            delete accToBeDeleted;
        }
    }
    global void finish(Database.BatchableContext bc){
        
    }
}

Right now if any of the duplicate records EIN matches suppose two accounts A1 and A2 with EIN 1 in both then the account having the phone number remains suppose only A1 has phone number then A1 is kept and A2 is removed. It is working this way but I have to add another condition..

Here if both have same EIN and same website then only the accounts should be merged and the one with the phonenumber should remain and the other should be deleted.

3
  • 1
    Reading your source code, I don't see how Accounts with Phones currently win over those without. Apparently Name, Phone, Website of the last Account found with a given EIN will overwrite these fields on the first Account, even if the Phone becomes null this way. Or does the tiny display of my cell phone mislead me? Aug 16, 2022 at 16:30
  • Or do I misunderstand your first paragraph and this is part of the requirement? Aug 16, 2022 at 16:33
  • @FelixvanHove Actually in the current code I am getting the phone number in the new merged account, but the initial condition was EIN should be same but right now I have to provide it such a way that EIN and website should be same.
    – PJ98
    Aug 16, 2022 at 16:50

1 Answer 1

1

Realistically, you should consider using Duplicate Rules to identify records that are duplicates. It would drastically improve performance and simplify your code.

In addition, you should be using Database.merge instead of Database.delete; your logic will remove all child elements as well (contacts, cases, opportunities, account relationships, custom relationships, etc), so you're potentially deleting data you don't want to remove.

Your code also doesn't consider the possibility that a single EIN may appear in two separate transactions.

Here's a revised form of your code:

public class DeduplicateAccounts implements Database.Batchable<sObject> {
    public Database.QueryLocator start(Database.BatchableContext context) {
        return Database.getQueryLocator([
            SELECT EIN__c FROM Account WHERE EIN__c != null
        ]);
    }
    public void execute(Database.BatchableContext context, List<Account> scope) {
        Set<String> accountEins = new Set<String>();
        for(Account record: scope) {
            accountEins.add(record.EIN__c);
        }
        Map<String, List<Account>> accountsByEins = new Map<String, List<Account>>();
        for(Account record: [SELECT FIELDS(STANDARD), EIN__c FROM Account WHERE EIN__c = :accountEins]) {
            List<Account> accounts = accountsByEins.get(record.EIN__c);
            if(accounts == null) {
                accounts = new List<Account>();
                accountsByEins.put(record.EIN__c, accounts);
            }
            accounts.add(record);
        }
        for(List<Account> dupAccountSource: accountsByEins.values()) {
            Map<String, Object> fieldValues = new Map<String, Object>();
            List<Account> dupAccounts = (List<Account>)Security.stripInaccessible(AccessType.UPDATABLE, dupAccountSource).getRecords();
            Account primaryAccount = dupAccounts.remove(0); // We choose one as primary
            for(Account dupAccount: dupAccounts) {
                // For all others, get the fields to copy
                fieldValues.putAll(dupAccount.getPopulatedFieldsAsMap());
            }
            // Copy fields to the primary record
            for(String field: fieldValues.keySet()) {
                primaryAccount.put(field, fieldValues.get(field));
            }
            Database.merge(primaryAccount, dupAccounts); // And merge the records
        }
    }
    public void finish(Database.BatchableContext context) {

    }
}

I wrote this while you were commenting, so in response, I'll simply add that if you want to match on multiple fields, you can read my previous answer on matching against multiple fields.

Obviously, there's no error handling, so some additional work is recommended. Also, remember that you will need to execute this batch in a smaller size than the default 200, or you'll run in to governor limits.

3
  • Method does not exist or incorrect signature: void values() from the type List<Account> An issue in the code
    – PJ98
    Aug 22, 2022 at 6:17
  • @PJ98 Yeah, I used the wrong variable. I've fixed it.
    – sfdcfox
    Aug 22, 2022 at 9:24
  • I corrected and tested but the duplicate accounts arent getting removed and the lookup fields or any data in the account remains same.
    – PJ98
    Aug 22, 2022 at 12:18

You must log in to answer this question.

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