function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Brooks Johnson 6Brooks Johnson 6 

Help with Bulk Patterns

Hi Friends,

I have a method that updates a lookup field on the task object. The task field is updated based on the value in a lookup field on the Contact. I  know it is inefficient and it fails my bulk test class. But I am not sure how to optimize the SOQL query to get my code under the  100 SOQL query limit. Any tips or pointers would be great.  The tasks are getting passed into the Method through trigger.new in a trigger handler. I am sure that putting the WhoIds into a list and doing a query to get the contacts and add them to a Map is pretty sub-optimal. 
public static void assignTasksToRelationshipOwnersInsert(List<Task> taskList) {
        //assign contacts owner to task look up field
        Set<Id> contactIds = new Set<Id>();
        for (Task t : taskList) {
            if (t.WhoId != null && t.Subject.contains('Pardot List Email')) {
                contactIds.add(t.WhoId);
            }
        }
        //create a map of contacts and regions
        //Fails SOQL limit here
        List<Contact> contactList = [
                SELECT Id, New_Relationship_owner1__c
                FROM CONTACT
                WHERE Id IN:contactIds
                AND New_Relationship_owner1__c != Null
        ];
        Map<Id, ID> contactMap = new Map<Id, Id>();
        for (Contact c : contactList) {
            contactMap.put(c.Id, c.New_Relationship_owner1__c);
        }
        for (Task updatedTask : taskList) {
            if (contactMap.containsKey(updatedTask.WhoId) && contactMap.size() > 0) {
                updatedTask.Relationship_Owner__c = contactMap.get(updatedTask.WhoId);
            }
        }
    }


 
Best Answer chosen by Brooks Johnson 6
Alain CabonAlain Cabon
Hi,

Integer numContacts = 200;  
for (Contact c : contacts){
     Task task1 = new Task(Subject = 'Pardot List Email', whoId = c.Id, ActivityDate = date.today());
     insert task1;
}
200 single insert for one synchronous transaction and the maximum is 150.

Total number of DML statements issued:150 max.
Calls to the following methods count against the number of DML statements issued in a request.
  • Approval.process
  • Database.convertLead
  • Database.emptyRecycleBin
  • Database.rollback
  • Database.setSavePoint
  • delete and Database.delete
  • insert and Database.insert
  • merge and Database.merge
  • undelete and Database.undelete
  • update and Database.update
  • upsert and Database.upsert
  • EventBus.publish
  • System.runAs
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm

All your code is bulkified excepted this insert in a loop probably.

A simple check when you overcome the governor limits is to look at all the DML statements in loops.

All Answers

Alain CabonAlain Cabon
Hi,

This code is correct (even if incomplete: updatedTask not used) but it is the caller of the class that must be wrong and you don't have posted it.
 
Brooks Johnson 6Brooks Johnson 6
Hi Alain,

I never thought the issue could be from the handler. Here is that part of the code. 
trigger TaskTriggerHandler on Task (
        before insert, after insert,
        before update, after update,
        before delete, after delete) {

    if (Trigger.isBefore) {
        if (Trigger.isInsert) {
            updateTaskLookup.assignTasksToRelationshipOwnersInsert(trigger.new);
            UpdateTaskLookup.assignTasksToRegionInsert(trigger.new);
            updateTaskLookup.assignTasksToDivisionInsert(trigger.new);
            updateTaskLookup.assignTasksToNationalInsert(trigger.new);

        }
Raj VakatiRaj Vakati
Hi 
can u give us the complete code for updateTaskLookup class .. 

So for the only optimization, i can see rewrite the updateTaskLookup handler class and from the code, you shared so for noting is wrong except firing multiple methods on before insert 
Brooks Johnson 6Brooks Johnson 6
Well, I feel better about my original solution now.   I am including the entire class, the trigger handler, and the test class. It fails when I run the bulk test method. 

The Class
/*This class assigns look up values to Pardot email tasks.
this is used by the count email and count unqiue email classes.
Created by Brooks Johnson -Athena Global Advisors
5 Nov 2018

 */


public class UpdateTaskLookup {

    public static void assignTasksToRelationshipOwnersInsert(List<Task> taskList) {
        //assign contacts owner to task look up field
        Set<Id> contactIds = new Set<Id>();
        for (Task t : taskList) {
            if (t.WhoId != null && t.Subject.contains('Pardot List Email')) {
                contactIds.add(t.WhoId);
            }
        }
        //create a map of contacts and regions
        List<Contact> contactList = [
                SELECT Id, New_Relationship_owner1__c
                FROM CONTACT
                WHERE Id IN:contactIds
                AND New_Relationship_owner1__c != Null
        ];
        Map<Id, ID> contactMap = new Map<Id, Id>();
        for (Contact c : contactList) {
            contactMap.put(c.Id, c.New_Relationship_owner1__c);
        }
        for (Task updatedTask : taskList) {
            if (contactMap.containsKey(updatedTask.WhoId) && contactMap.size() > 0) {
                updatedTask.Relationship_Owner__c = contactMap.get(updatedTask.WhoId);
            }
        }
    }

    public static void assignTasksToRegionInsert(List<Task> taskList) {
        //assign   contacts region to tasks.
        //get contact Ids from tasks
        Set<Id> contactIds = new Set<Id>();
        for (Task t : taskList) {
            if (t.WhoId != null && t.Subject.contains('Pardot List Email')) {
                contactIds.add(t.WhoId);
                System.debug('Tasks in trigger = ' + taskList.size());
                System.debug('Contacts found = ' + contactIds.size());
            }
        }
        //create a map of contact id and region id key value pairs
        List<Contact> contactList = [
                SELECT Id, Region_Lookup__c
                FROM Contact
                WHERE Id IN:contactIds
                AND Region_Lookup__c != Null
        ];
        Map<Id, ID> contactMap = new Map<Id, Id>();
        for (Contact c : contactList) {
            contactMap.put(c.Id, c.Region_Lookup__c);
            System.debug('Values added to map = ' + contactMap.size());
        }
        //assign value of contact region to task
        for (Task updatedTask : taskList) {

            If (contactMap.containsKey(updatedTask.WhoId)) {
                updatedTask.Region__c = contactMap.get(updatedTask.WhoId);
            }
        }
    }

    public static void assignTasksToDivisionInsert(List<Task> taskList){

        //assign   contacts Division to tasks.
        //get contact Ids from tasks
        Set<Id> contactIds = new Set<Id>();
        for (Task t : taskList) {
            if (t.WhoId != null && t.Subject.contains('Pardot List Email')) {
                contactIds.add(t.WhoId);
                System.debug('Tasks in trigger = ' + taskList.size());
                System.debug('Contacts found = ' + contactIds.size());
            }
        }
        //create a map of contact id and region id key value pairs
        List<Contact> contactList = [
                SELECT Id, Division_Lookup__c
                FROM Contact
                WHERE Id IN:contactIds
                AND Division_Lookup__c != Null
        ];
        Map<Id, ID> contactMap = new Map<Id, Id>();
        for (Contact c : contactList) {
            contactMap.put(c.Id, c.Division_Lookup__c);
            System.debug('Values added to map = ' + contactMap.size());
        }
        //assign value of contact region to task
        for (Task updatedTask : taskList) {

            If (contactMap.containsKey(updatedTask.WhoId)) {
                updatedTask.Division__c = contactMap.get(updatedTask.WhoId);
            }
        }

    }

    public static void assignTasksToNationalInsert(List<Task> taskList){

        //get Id for National -SHould never be more than one National Object
        //National__c nationalId = [SELECT Id FROM National__c LIMIT 1];
        List<Task> tasksToUpdate = new List<Task>();

        //update field on tasks
        For(Task t : taskList){
            if (t.Subject.Contains('Pardot List Email') && t.Region__c != null) {
                t.national__c = 'a1G18000000yYFLEA2';

            }

        }


    }

}

The Trigger Handler
/**
 * Created by Brooks Johnson on 11/2/2018.
 */



trigger TaskTriggerHandler on Task (
        before insert, after insert,
        before update, after update,
        before delete, after delete) {

    if (Trigger.isBefore) {
        if (Trigger.isInsert) {
            updateTaskLookup.assignTasksToRelationshipOwnersInsert(trigger.new);
            UpdateTaskLookup.assignTasksToRegionInsert(trigger.new);
            updateTaskLookup.assignTasksToDivisionInsert(trigger.new);
            updateTaskLookup.assignTasksToNationalInsert(trigger.new);

        }
        if (Trigger.isUpdate) {
            // Call class logic here!
        }
        if (Trigger.isDelete) {
            // Call class logic here!
        }
    }

    if (Trigger.IsAfter) {
        if (Trigger.isInsert) {
            // Call class logic here!
        }
        if (Trigger.isUpdate) {
            // Call class logic here!
        }
        if (Trigger.isDelete) {
            // Call class logic here!
        }
    }
}

And the test class
@IsTest
public class UpdateTaskLookup_Test {

    @IsTest
    public static void oneTaskFound(){
        Account a = new Account(Name = 'Test Account');
        insert a;
        National__c national = new National__c();
        insert national;
        Division__c division = new Division__c(Name = 'Test Division');
        insert division;
        Region__c region = new region__c(Name = 'Test Region', divisions__c = division.Id);
        insert region;
        Contact contact = new Contact(FirstName = 'Test', LastName = 'Test', AccountId = a.id, region_lookup__c = region.Id,
                Division_Lookup__c  = division.Id);
        insert contact;
        Contact c = [SELECT Id, region_lookup__c, Division_Lookup__c, New_Relationship_owner1__c FROM Contact
        LIMIT 1];
        Task task = new Task(WhoId = c.Id, Subject = 'Pardot List Email');
        insert task;
        National__c nationalId = [SELECT Id FROM National__c LIMIT 1];
        Task updatedTask = [SELECT Region__c, relationship_owner__c, Division__c, National__c FROM Task LIMIT 1];
        system.assertEquals(c.Region_Lookup__c, updatedTask.Region__c);
        System.assertEquals(c.New_Relationship_owner1__c, updatedTask.relationship_owner__c);
        System.assertEquals(c.Division_Lookup__c, updatedTask.Division__c);
        //System.assertEquals(nationalId.Id, updatedTask.National__c);
    }
    @IsTest
    public static void noTasksFound(){
        Account a = new Account(Name = 'Test Account');
        insert a;
        National__c national = new National__c();
        insert national;
        Division__c division = new Division__C(Name = 'Test Division');
        Insert division;
        region__c region = new region__c(Name = 'Test Region', divisions__c = division.Id);
        insert region;
        Contact contact = new Contact(FirstName = 'Test', LastName = 'Test', AccountId = a.id, region_lookup__c = region.Id,
                Division_Lookup__c  = division.Id);
        insert contact;
        Contact c = [SELECT Id, region_lookup__c, Division_Lookup__c, New_Relationship_owner1__c FROM Contact
        LIMIT 1];
        Task task = new Task(WhoId = c.Id, Subject = 'Hey Do This');
        insert task;
        National__c nationalId = [SELECT Id FROM National__c LIMIT 1];
        Task updatedTask = [SELECT region__c, relationship_owner__c, Division__c, National__c FROM Task LIMIT 1];
        system.assertEquals(null, updatedTask.Region__c);
        System.assertEquals(null, updatedTask.relationship_owner__c);
        System.assertEquals(null, updatedTask.Division__c);
        System.assertEquals(null, updatedTask.National__c);
    }
    @IsTest
    public static void taskUpdateBulk(){
        Integer numRegions = 20;
        Integer numDivision = 3;
        Integer numContacts = 200;
        Integer numTasks = 20;

        List<Region__c> regions = new List<Region__c>();
        List<Contact> contacts = new List<Contact>();
        List<Task> tasks = new List<Task>();

        Account a = new Account(Name = 'Test Account');
        insert a;
        National__c national = new National__c();
        insert national;
        Division__c Div1 = new Division__c(Name = 'Test Division');
        insert Div1;
        Region__c region = new Region__c(Name = 'Test Region', Divisions__c = Div1.Id);
        insert region;
        relationship_owner__c owner = new relationship_owner__c(Name = 'Test Owner');
        insert owner;

        for(Integer i = 0; i < numContacts; i++){
            Contact con = new Contact(LastName = 'Test' + i, AccountId = a.Id, Region_Lookup__c = region.Id,
                                      Division_Lookup__c = Div1.Id, New_Relationship_owner1__c = owner.Id );
            contacts.add(con);
        }
        insert contacts;

        for (Contact c : contacts){
            Task task1 = new Task(Subject = 'Pardot List Email', whoId = c.Id, ActivityDate = date.today());
            insert task1;

            }
            test.startTest();
            insert tasks;
            test.stopTest();
            Contact updatedContact = [Select Id, Region_Lookup__c, Division_Lookup__c  FROM Contact LIMIT 1];
            Task updatedTask = [SELECT WhoId, Region__c, Division__c, Relationship_Owner__c FROM TASK
            WHERE WhoId =: updatedContact.Id  LIMIT 1];
        }
        //System.assertEquals(updatedContact.Region_lookup__C, updatedTask.Region__c );
    }

Thank you for taking the time to look at this. 
 
Alain CabonAlain Cabon
Hi,

Integer numContacts = 200;  
for (Contact c : contacts){
     Task task1 = new Task(Subject = 'Pardot List Email', whoId = c.Id, ActivityDate = date.today());
     insert task1;
}
200 single insert for one synchronous transaction and the maximum is 150.

Total number of DML statements issued:150 max.
Calls to the following methods count against the number of DML statements issued in a request.
  • Approval.process
  • Database.convertLead
  • Database.emptyRecycleBin
  • Database.rollback
  • Database.setSavePoint
  • delete and Database.delete
  • insert and Database.insert
  • merge and Database.merge
  • undelete and Database.undelete
  • update and Database.update
  • upsert and Database.upsert
  • EventBus.publish
  • System.runAs
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm

All your code is bulkified excepted this insert in a loop probably.

A simple check when you overcome the governor limits is to look at all the DML statements in loops.
This was selected as the best answer
Brooks Johnson 6Brooks Johnson 6
Embarring that I never thought to look in a test class for the problem. thank you, everyone, for looking at this.