+ Start a Discussion
Justin GeorgeJustin George 

Help to Bulkify Trigger

Hello, I have written a trigger that autmatically creates a Task record when a custom object record is created. The Owner of the Task is located through a string of related lookups. Currently, the trigger works, but it is not bulkified. I have attempted to bulkify the trigger, but I got lost is the logic and I could not find a similar piece of sample code to reference. Can someone point me in the right direction or give me some guidance.
 
trigger AutoCreateTask on Investigation__c (after insert) {

  List<Task> tasks = new List<Task>();

    for (Investigation__c newInv: Trigger.New) {

      Investigation__c investigation = [Select Id, RecordType.Name, Driver__c, Employee__c from Investigation__c where Id =: newInv.Id];


                
        if (investigation.RecordType.Name == 'Auto Investigation') {   
          Contact driver = [select Supervisor__c FROM Contact where Id =: investigation.Driver__c];
          Tasks.add(new Task( OwnerId = driver.Supervisor__c,
                              WhatId  = newInv.Id,
                              Subject = 'Please complete driver investigaiton',
                              ActivityDate = System.today().addDays(14),
                              Type = 'Investigation'
                              ));
        }

        if (investigation.RecordType.Name == 'Injury Investigations') {   
          Contact employee = [select Supervisor__c FROM Contact where Id =: investigation.Employee__c];
          Tasks.add(new Task( OwnerId = employee.Supervisor__c,
                              WhatId  = newInv.Id,
                              Subject = 'Please complete injury investigaiton',
                              ActivityDate = System.today().addDays(2),
                              Type = 'Investigation'
                              ));
        }    
    }    
  insert tasks;      
}

 
Best Answer chosen by Justin George
Himanshu ParasharHimanshu Parashar
Hi Justin,

While bulkfying any trigger always think through the data which you need while running inside Trigger.new

We can see that you need data from Contact object and you have contact id as investigation_driver__c or investigation__c.employee__c which making this common and it can come out from Trigger.new loop.

so fix this issue we will follow below approach

1. Get all the contact id first from Trigger.new
2. SOQL contact object and prepare a dataset
3. Loop over Trigger.new and fill data from dataset created in step 2

Go though this code and try to understand logic with inline comments.
 
set<id> setContact = new set<id>();
//get all the contact ids
for(Investigation__c investigation : Trigger.new)
{
    if(investigation.driver__c!=null)
        setContact.add(investigation.driver__c);
        
    if(investigation.Employee__c!=null)
        setContact.add(investigation.Employee__c);
}

//Create Map which will be used for rest of the code.
Map<id,Contact> mapConatact = new Map<id,Contact>([select Supervisor__c FROM Contact where Id : in setContact])


//process trigger.new and create task

    List<Task> tasks = new List<Task>();

    for (Investigation__c newInv: Trigger.New) {

        if (investigation.RecordType.Name == 'Auto Investigation') {   
          Tasks.add(new Task( OwnerId = mapConatact.get(investigation.Driver__c).Supervisor__c,
                              WhatId  = newInv.Id,
                              Subject = 'Please complete driver investigaiton',
                              ActivityDate = System.today().addDays(14),
                              Type = 'Investigation'
                              ));
        }

        if (investigation.RecordType.Name == 'Injury Investigations') {   
          Contact employee = [select Supervisor__c FROM Contact where Id =: investigation.Employee__c];
          Tasks.add(new Task( OwnerId = mapConatact.get(investigation.Employee__c).Supervisor__c,
                              WhatId  = newInv.Id,
                              Subject = 'Please complete injury investigaiton',
                              ActivityDate = System.today().addDays(2),
                              Type = 'Investigation'
                              ));
        }    
    }

    insert tasks;

}

Let me know if you see any issue


Thanks,
Himanshu
Salesforce Certified Developer, Administrator, Service Cloud Consultant
P.S. If my answer helps you to solve your problem please mark it as best answer. It will help other to find best answer.
 

All Answers

Himanshu ParasharHimanshu Parashar
Hi Justin,

While bulkfying any trigger always think through the data which you need while running inside Trigger.new

We can see that you need data from Contact object and you have contact id as investigation_driver__c or investigation__c.employee__c which making this common and it can come out from Trigger.new loop.

so fix this issue we will follow below approach

1. Get all the contact id first from Trigger.new
2. SOQL contact object and prepare a dataset
3. Loop over Trigger.new and fill data from dataset created in step 2

Go though this code and try to understand logic with inline comments.
 
set<id> setContact = new set<id>();
//get all the contact ids
for(Investigation__c investigation : Trigger.new)
{
    if(investigation.driver__c!=null)
        setContact.add(investigation.driver__c);
        
    if(investigation.Employee__c!=null)
        setContact.add(investigation.Employee__c);
}

//Create Map which will be used for rest of the code.
Map<id,Contact> mapConatact = new Map<id,Contact>([select Supervisor__c FROM Contact where Id : in setContact])


//process trigger.new and create task

    List<Task> tasks = new List<Task>();

    for (Investigation__c newInv: Trigger.New) {

        if (investigation.RecordType.Name == 'Auto Investigation') {   
          Tasks.add(new Task( OwnerId = mapConatact.get(investigation.Driver__c).Supervisor__c,
                              WhatId  = newInv.Id,
                              Subject = 'Please complete driver investigaiton',
                              ActivityDate = System.today().addDays(14),
                              Type = 'Investigation'
                              ));
        }

        if (investigation.RecordType.Name == 'Injury Investigations') {   
          Contact employee = [select Supervisor__c FROM Contact where Id =: investigation.Employee__c];
          Tasks.add(new Task( OwnerId = mapConatact.get(investigation.Employee__c).Supervisor__c,
                              WhatId  = newInv.Id,
                              Subject = 'Please complete injury investigaiton',
                              ActivityDate = System.today().addDays(2),
                              Type = 'Investigation'
                              ));
        }    
    }

    insert tasks;

}

Let me know if you see any issue


Thanks,
Himanshu
Salesforce Certified Developer, Administrator, Service Cloud Consultant
P.S. If my answer helps you to solve your problem please mark it as best answer. It will help other to find best answer.
 
This was selected as the best answer
Justin GeorgeJustin George
Thanks Himanshu! I'm still having difficulty understanding the Map<> portion and setting the OwnerId based off of the Map<>. Do you mind explaining?
Himanshu ParasharHimanshu Parashar
Do you have understanding about the map works?
Justin GeorgeJustin George
I don't have a strong understanding. I know a map is key, value list. So in the case of Line 13: 

Map<id,Contact> mapConatact = new Map<id,Contact>([select Supervisor__c FROM Contact where Id : insetContact])

The map is storing the Contact.Id and the Supervisor__c
Himanshu ParasharHimanshu Parashar
Let's first understand Map:

Map is the collection of key value pair. so when we define
Map<String,String> map = new Map<String,String>();
it can contain id as key and any string as value, we can initialize this map using
map.put('US','USA');
map.put('JP','JAPAN');
and when we want to use it we can use following syntax.
 
map.get('US');
and it will output USA

in the similiar fashion we can create a map of any type where key will be unique.

So in your case I have created a map of id,Contact sObject in following way
 
Map<id,Contact> mapContact = new Map<id,Contact>();
which we can initialize in following way
for(Contact con : [select id from contact where id in : setContact])
{
   mapContact.put(con.id,con);
}

above syntax has a small performance issue, if there are 100+ contact above code execution will happen 100 times which is not optimized way to do this so SFDC provides single statement initialization which automatically create map of id,sObject which i did in your code as shown below
 
Map<id,Contact> mapContact = new Map<id,Contact>([select Supervisor__c FROM Contact where Id in : setContact]);

so instead of story only Supervisor__c in map as a value I am storing whole Contact object as value which I can access by passing contact id in following way
mapContact.get(investigation.Driver__c).Supervisor__c
//alternative way (assign to contact object and aftet that use it)
Contact objContact = mapContact.get(investigation.Driver__c);
OwnerId = objContact.Supervisor__c

There are different way to write this code but I have written this code consideirng all best practice of trigger so that you can understand trigger concepts.

Makes sense?

Thanks,
Himanshu
 
Justin GeorgeJustin George
Thanks again Himanshu! That was very helpful. I am bookmarking this post for future reference.
Justin GeorgeJustin George
Hi Himanshu,

I made some changes to the code so that it would save (clean up variable names, etc....). The trigger saves, but I can't get it to fire. I double checked the record type names and those are correct. Can you take a look at the code to see why it is not creating a task?
 
trigger AutoCreateTask on Investigation__c (after insert) {

    set<id> setContact = new set<id>();
    
    for(Investigation__c investigation : Trigger.new){
        
        if(investigation.driver__c != null){
            setContact.add(investigation.driver__c);
        }
            
        if(investigation.Employee__c != null){
            setContact.add(investigation.Employee__c);
        }
    }
    
    Map<id,Contact> mapContact = new Map<id,Contact>([select Supervisor__c FROM Contact where Id in: setContact]);

    List<Task> tasks = new List<Task>();

        for (Investigation__c newInv : Trigger.New) {
    
            if (newInv.RecordType.Name == 'Auto Investigation') {   
                Tasks.add(new Task( OwnerId = mapContact.get(newInv.Driver__c).Supervisor__c,
                                    WhatId  = newInv.Id,
                                    Subject = 'Please complete driver investigation',
                                    ActivityDate = System.today().addDays(14),
                                    Type = 'Investigation'
                                    ));
            }
    
            if (newInv.RecordType.Name == 'Injury Investigations') {   
                Tasks.add(new Task( OwnerId = mapContact.get(newInv.Employee__c).Supervisor__c,
                                    WhatId  = newInv.Id,
                                    Subject = 'Please complete injury investigation',
                                    ActivityDate = System.today().addDays(2),
                                    Type = 'Investigation'
                                    ));
            }    
        }
    insert tasks;
}

 
Himanshu ParasharHimanshu Parashar
Do you have value in investigation.driver__c ?
Justin GeorgeJustin George
Yes Driver__c is populated. Here's the log:
11:41:18.0 (11001157)|CODE_UNIT_FINISHED|ReassignOwnerInvestigation on Investigation trigger event BeforeInsert for [new]
11:41:18.0 (110681995)|CODE_UNIT_STARTED|[EXTERNAL]|01q550000004L3i|AutoCreateTask on Investigation trigger event AfterInsert for [a1C55000000EBdw]
11:41:18.0 (110717034)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:8
11:41:18.0 (110976457)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:16
11:41:18.0 (110998261)|VARIABLE_SCOPE_BEGIN|[1]|this|AutoCreateTask|true|false
11:41:18.0 (111089919)|VARIABLE_ASSIGNMENT|[1]|this|{}|0x5cd26bf1
11:41:18.0 (111154930)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:16
11:41:18.0 (111164700)|VARIABLE_SCOPE_BEGIN|[1]|this|AutoCreateTask|true|false
11:41:18.0 (111181812)|VARIABLE_ASSIGNMENT|[1]|this|{}|0x5cd26bf1
11:41:18.0 (111190492)|STATEMENT_EXECUTE|[1]
11:41:18.0 (111193235)|STATEMENT_EXECUTE|[3]
11:41:18.0 (111204326)|HEAP_ALLOCATE|[3]|Bytes:4
11:41:18.0 (111323773)|VARIABLE_ASSIGNMENT|[3]|this.setContact|{"s":1,"v":[]}|0x5cd26bf1
11:41:18.0 (111417828)|HEAP_ALLOCATE|[5]|Bytes:5
11:41:18.0 (111456787)|VARIABLE_SCOPE_BEGIN|[5]|investigation|Investigation__c|true|false
11:41:18.0 (113037228)|VARIABLE_ASSIGNMENT|[5]|investigation|{"LastModifiedDate":"2016-01-13T19:41:18.000Z","Tools_Equipment_Desi (5 more) ...":false,"Poor_Equipment_or_To (15 more) ...":false,"Are_all_metal_bearin (16 more) ...":false,"Body_Fatigue__c":false,"Change_Mgt_failure__ (1 more) ...":false,"qVar1__c":false,"Is_any_Aux_Equip_in_ (17 more) ...":false,"Stress__c":false,"Name":"I-1220","Are_rungs_free_of_oi (4 more) ...":false,"CreatedById":"0051a000000m0DqAAI","Poor_Maintenance_or_ (9 more) ...":false,"Step_and_side_rail__ (1 more) ...":false,"Material_Failure__c":false,"Unable_to_Meet_Physi (22 more) ...":false,"Process_Teamwork_Bre (9 more) ...":false,"Defective_Raw_Materi (5 more) ...":false,"Drug_Screen_Req__c":false,"IsDeleted":false,"qVar2__c":false,"Mental_Fatigure__c":false,"Wrong_Type_for_Job__ (1 more) ...":false,"Lack_of_Raw_Material (3 more) ...":false,"Defective_Equipment_ (10 more) ...":false,"Lack_of_Management_I (13 more) ...":false,"Hazard_Recognition__ (1 more) ...":false,"Tools_Equipment_Fail (6 more) ...":false,"Are_rungs_dented__c":false,"Inattention_to_Task_ (2 more) ...":false,"Lack_of_Process__c":false,"Is_rope_in_good_cond (8 more) ...":false,"CreatedDate":"2016-01-13T19:41:18.000Z","Lack_of_Hazard_Recog (9 more) ...":false,"Driver__c":"00355000006Ks08AAC","Id":"a1C55000000EBdwEAG","Incorrect_Tool_Selec (7 more) ...":false,"Maint_Requirements_N (18 more) ...":false,"Lack_of_Communicatio (4 more) ...":false,"qVar3__c":false,"Employee_Judgement__ (1 more) ...":false,"Ergonomics_Body_Posi (7 more) ...":false,"Tools_Equipment_Not_ (16 more) ...":false,"Improper_Motivation_ (2 more) ...":false,"Are_the_safety_feet_ (20 more) ...":false,"Personal_Protective_ (12 more) ...":false,"Weather_Temperature_ (11 more) ...":false,"OwnerId":"0051a000000m0DqAAI","Not_Following_Safe_P (11 more) ...":false,"Are_side_rails_dente (4 more) ...":false,"RecordTypeId":"012550000000CQkAAM","Procedure_different_ (22 more) ...":false,"Lack_of_Knowledge__c":false,"Lack_of_Skill__c":false,"Are_the_ID_numbers_i (19 more) ...":false,"Are_the_bottoms_of_t (18 more) ...":false,"Poor_Communication__ (1 more) ...":false,"Loudness_or_Noise_Po (10 more) ...":false,"Do_movable_parts_ope (14 more) ...":false,"Poor_Job_Design_and_ (20 more) ...":false,"qVar4__c":false,"Is_the_rated_load_ca (21 more) ...":false,"Lack_of_Training_Edu (9 more) ...":false,"Tools_Equipment_Not_ (12 more) ...":false,"Task_Hazards_Not_Dea (19 more) ...":false,"SystemModstamp":"2016-01-13T19:41:18.000Z","Investigation_Number (3 more) ...":"1221","Forces_of_Nature__c":false,"Inadequate_Capabilit (4 more) ...":false,"All_hw_fittings_secu (16 more) ...":false,"No_or_Poor_Procedure (4 more) ...":false,"Disordered_Workplace (3 more) ...":false,"Is_ladder_stored_out (19 more) ...":false,"Surfaces_Poorly_Main (9 more) ...":false,"LastModifiedById":"0051a000000m0DqAAI","Failure_to_Identify_ (9 more) ...":false}|0x5f5583bd
11:41:18.0 (113055896)|STATEMENT_EXECUTE|[5]
11:41:18.0 (113116217)|STATEMENT_EXECUTE|[7]
11:41:18.0 (113125884)|STATEMENT_EXECUTE|[11]
11:41:18.0 (113142622)|HEAP_ALLOCATE|[5]|Bytes:5
11:41:18.0 (113157990)|VARIABLE_ASSIGNMENT|[5]|investigation|null|
11:41:18.0 (113162255)|STATEMENT_EXECUTE|[16]
11:41:18.0 (113169568)|HEAP_ALLOCATE|[16]|Bytes:4
11:41:18.0 (113175028)|HEAP_ALLOCATE|[16]|Bytes:53
11:41:18.0 (113179844)|HEAP_ALLOCATE|[16]|Bytes:4
11:41:18.0 (113236491)|HEAP_ALLOCATE|[16]|Bytes:4
11:41:18.0 (113245096)|HEAP_ALLOCATE|[16]|Bytes:4
11:41:18.0 (115517820)|SOQL_EXECUTE_BEGIN|[16]|Aggregations:0|SELECT Supervisor__c FROM Contact WHERE Id = :tmpVar1
11:41:18.0 (117150440)|SOQL_EXECUTE_END|[16]|Rows:0
11:41:18.0 (117170750)|HEAP_ALLOCATE|[16]|Bytes:4
11:41:18.0 (117183069)|HEAP_ALLOCATE|[16]|Bytes:0
11:41:18.0 (117223602)|HEAP_ALLOCATE|[16]|Bytes:4
11:41:18.0 (117289029)|VARIABLE_ASSIGNMENT|[16]|this.mapContact|{"s":1,"v":{}}|0x5cd26bf1
11:41:18.0 (117295510)|STATEMENT_EXECUTE|[18]
11:41:18.0 (117302868)|HEAP_ALLOCATE|[18]|Bytes:4
11:41:18.0 (117409092)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
11:41:18.0 (117426821)|VARIABLE_ASSIGNMENT|[18]|this.tasks|{"s":1,"v":[]}|0x5cd26bf1
11:41:18.0 (117477214)|HEAP_ALLOCATE|[20]|Bytes:5
11:41:18.0 (117498645)|VARIABLE_SCOPE_BEGIN|[20]|newInv|Investigation__c|true|false
11:41:18.0 (118321081)|VARIABLE_ASSIGNMENT|[20]|newInv|{"LastModifiedDate":"2016-01-13T19:41:18.000Z","Tools_Equipment_Desi (5 more) ...":false,"Poor_Equipment_or_To (15 more) ...":false,"Are_all_metal_bearin (16 more) ...":false,"Body_Fatigue__c":false,"Change_Mgt_failure__ (1 more) ...":false,"qVar1__c":false,"Is_any_Aux_Equip_in_ (17 more) ...":false,"Stress__c":false,"Name":"I-1220","Are_rungs_free_of_oi (4 more) ...":false,"CreatedById":"0051a000000m0DqAAI","Poor_Maintenance_or_ (9 more) ...":false,"Step_and_side_rail__ (1 more) ...":false,"Material_Failure__c":false,"Unable_to_Meet_Physi (22 more) ...":false,"Process_Teamwork_Bre (9 more) ...":false,"Defective_Raw_Materi (5 more) ...":false,"Drug_Screen_Req__c":false,"IsDeleted":false,"qVar2__c":false,"Mental_Fatigure__c":false,"Wrong_Type_for_Job__ (1 more) ...":false,"Lack_of_Raw_Material (3 more) ...":false,"Defective_Equipment_ (10 more) ...":false,"Lack_of_Management_I (13 more) ...":false,"Hazard_Recognition__ (1 more) ...":false,"Tools_Equipment_Fail (6 more) ...":false,"Are_rungs_dented__c":false,"Inattention_to_Task_ (2 more) ...":false,"Lack_of_Process__c":false,"Is_rope_in_good_cond (8 more) ...":false,"CreatedDate":"2016-01-13T19:41:18.000Z","Lack_of_Hazard_Recog (9 more) ...":false,"Driver__c":"00355000006Ks08AAC","Id":"a1C55000000EBdwEAG","Incorrect_Tool_Selec (7 more) ...":false,"Maint_Requirements_N (18 more) ...":false,"Lack_of_Communicatio (4 more) ...":false,"qVar3__c":false,"Employee_Judgement__ (1 more) ...":false,"Ergonomics_Body_Posi (7 more) ...":false,"Tools_Equipment_Not_ (16 more) ...":false,"Improper_Motivation_ (2 more) ...":false,"Are_the_safety_feet_ (20 more) ...":false,"Personal_Protective_ (12 more) ...":false,"Weather_Temperature_ (11 more) ...":false,"OwnerId":"0051a000000m0DqAAI","Not_Following_Safe_P (11 more) ...":false,"Are_side_rails_dente (4 more) ...":false,"RecordTypeId":"012550000000CQkAAM","Procedure_different_ (22 more) ...":false,"Lack_of_Knowledge__c":false,"Lack_of_Skill__c":false,"Are_the_ID_numbers_i (19 more) ...":false,"Are_the_bottoms_of_t (18 more) ...":false,"Poor_Communication__ (1 more) ...":false,"Loudness_or_Noise_Po (10 more) ...":false,"Do_movable_parts_ope (14 more) ...":false,"Poor_Job_Design_and_ (20 more) ...":false,"qVar4__c":false,"Is_the_rated_load_ca (21 more) ...":false,"Lack_of_Training_Edu (9 more) ...":false,"Tools_Equipment_Not_ (12 more) ...":false,"Task_Hazards_Not_Dea (19 more) ...":false,"SystemModstamp":"2016-01-13T19:41:18.000Z","Investigation_Number (3 more) ...":"1221","Forces_of_Nature__c":false,"Inadequate_Capabilit (4 more) ...":false,"All_hw_fittings_secu (16 more) ...":false,"No_or_Poor_Procedure (4 more) ...":false,"Disordered_Workplace (3 more) ...":false,"Is_ladder_stored_out (19 more) ...":false,"Surfaces_Poorly_Main (9 more) ...":false,"LastModifiedById":"0051a000000m0DqAAI","Failure_to_Identify_ (9 more) ...":false}|0x5f5583bd
11:41:18.0 (118339962)|STATEMENT_EXECUTE|[20]
11:41:18.0 (118352980)|STATEMENT_EXECUTE|[22]
11:41:18.0 (118361815)|STATEMENT_EXECUTE|[31]
11:41:18.0 (118378047)|HEAP_ALLOCATE|[20]|Bytes:5
11:41:18.0 (118392301)|VARIABLE_ASSIGNMENT|[20]|newInv|null|
11:41:18.0 (118396172)|STATEMENT_EXECUTE|[40]
11:41:18.0 (118551465)|HEAP_ALLOCATE|[EXTERNAL]|Bytes:4
11:41:18.118 (118571666)|CUMULATIVE_LIMIT_USAGE
11:41:18.118 (118571666)|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 2 out of 100
  Number of query rows: 0 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 0 out of 150
  Number of DML rows: 0 out of 10000
  Maximum CPU time: 0 out of 10000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

11:41:18.118 (118571666)|CUMULATIVE_LIMIT_USAGE_END

 
Himanshu ParasharHimanshu Parashar
Create a new formula field Record_TypeName__c and put RecordType.Name in it because relationships values are not available inside trigger which is making Recordtype.Name null.

replace following line
newInv.RecordType.Name
with 
newInv.Record_TypeName__c

It should resolve your problem.


Thanks,
Himanshu