I wrote a batch process over the weekend that looks at a child record's date field and updates the corresponding date on the parent record. I originally wrote it as a bunch of if statements and then added the parent id and field to a list. When I ran the batch today I get an error in debug log:

"Duplicate id in list"

This makes me think I need to add the values from the if statements to a map where I can update the record once with all the fields that it needs instead of updating the record multiple times for each possible value.

Here is a snip of batch code I have, how can I adjust to update a record once instead of multiple times.

global void execute(Database.BatchableContext BC, List<Revenue_Pipeline_Schedule__c> revPipeSchedule) {
    Map<Id, Revenue_Pipeline__c> revPipe = new Map<Id, Revenue_Pipeline__c>();

    for(Revenue_Pipeline_Schedule__c rPS : revPipeSchedule){
        if(rPS.Date__c == date.newinstance(2014,10,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Oct2014__c=rPS.Amount__c));}
        if(rPS.Date__c == date.newinstance(2014,11,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Nov2014__c=rPS.Amount__c));}
        if(rPS.Date__c == date.newinstance(2014,12,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Dec2014__c=rPS.Amount__c));}       
        if(rPS.Date__c == date.newinstance(2015,01,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Jan2015__c=rPS.Amount__c));}
        if(rPS.Date__c == date.newinstance(2015,02,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Feb2015__c=rPS.Amount__c));}
        if(rPS.Date__c == date.newinstance(2015,03,1)){ revPipe.put(rPS.Revenue_Pipeline__c, Mar2015__c=rPS.Amount__c));}
        .... 47 lines later
update revPipe;
  • Shoot I still had it for when it was a list, I'll adjust. – Dan Wooding Jan 9 '17 at 20:53
  • I'm getting stuck, I get "Variable does not exist: Oct2014__c" when I try to save. I tried adding revPipe.Oct2014__c but then get 'Invalid field initializer' – Dan Wooding Jan 9 '17 at 20:58
  • How locked into this architecture are you? Any time you're making 47 parallel updates...it's pretty messed up. You should try to pursue something more scalable if you have any liberty to do so. – Adrian Larson Jan 9 '17 at 21:01
  • I'm sort of locked, chat? – Dan Wooding Jan 9 '17 at 21:03
  • Sure, let's chat... – Adrian Larson Jan 9 '17 at 21:09

Any time you need to use a map for de-duplication, the basic idea is:

Map<Id, MyObject__c> records = new Map<Id, MyObject__c>();
records.put(someMyObjectId, new MyObject__c(/*data*/));

However, in your case it is more complicated because you want to set multiple fields on the same record, so a scheme where you only use put won't work, because you will clobber your own updates. You can circumvent this problem, however, if you simply initialize all the keys first.

Map<Id, Revenue_Pipeline__c> pipelines = new Map<Id, Revenue_Pipeline__c>();
for (Revenue_Pipeline_Schedule__c schedule : scope)
    Id pipelineId = schedule.Revenue_Pipeline__c;
    pipelines.put(pipelineId, new Revenue_Pipeline__c(Id=pipelineId));
// now all keys are initialized

for (Revenue_Pipeline_Schedule__c schedule : scope)
    if (/*condition*/)
        pipelines.get(schedule.Revenue_Pipeline__c).put(/*field*/, /*value*/);

A note on your absurdly long if chain: you can almost always reduce these unwieldy bits of logic to use a Map. In this case, I would do a Map<Date, SObjectField> that tells you where to put the data.

static Map<Date, SObjectField> dateToField = new Map<Date, SObjectField>
    Date.newInstance(2014, 10, 1) => Revenue_Pipeline__c.Oct2014__c,
    Date.newInstance(2014, 11, 1) => Revenue_Pipeline__c.Nov2014__c,
    Date.newInstance(2014, 12, 1) => Revenue_Pipeline__c.Dec2014__c,
    // etc...

Then your above code can further simplify to just a few lines:

for (Revenue_Pipeline_Schedule__c schedule : scope)
    SObjectField field = dateToField.get(schedule.Date__c);
    if (field != null)
        pipelines.get(schedule.Revenue_Pipeline__c).put(field, schedule.Amount__c);
  • Do you still need a DML statement after the for loop? Example: update pipelines? – Dan Wooding Jan 11 '17 at 4:23
  • Yes, you'll still need to actually perform the update. – Adrian Larson Jan 11 '17 at 4:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.