You need to sign in to do that
Don't have an account?
BrianWK
Help Suggestions greatly appreciative. If anyone has a good resources that gives me examples of similar triggers that would be fantastic learning tool for me. Thank you!
Overcoming SoQL Limits
Hi everyone. I'm still very new at writing triggers. I've had a few succesful ones. We have one trigger that is firing on a custom object and updates two fields pulled via SoQL from its related object. The code below works well but only updates 1 record when using the dataloader.
Code:
trigger Product_Contact_Roles_Phone_Email on Product_Contact_Role__c (before insert, before update) { //Set Contact_Email__c to Email //Set Contact_Phone__c to Phone //This should set a variable to an identifier String ContactID = Trigger.new[0].Contact__c; Contact p = [SELECT Phone FROM Contact WHERE contact.id = :ContactID limit 1]; Contact e = [SELECT Email FROM Contact WHERE contact.id = :ContactID limit 1]; //For the Item that's update the field Phone__C value is set to the varible defined above Trigger.new[0].Contact_Phone__C = p.Phone; Trigger.new[0].Contact_Email__C = e.Email;
So I updated the code to loop through the Trigger.new. This works, but once I get more than 10 records being updated I reach the limit. I think I can use a map to grab all the Phone and E-mails from the related contact IDs of items in Trigger.new and then use a for to loop through all Ids from Trigger.new to update from the Map. But 1. I'm not sure if this would work and 2. if it would not quite sure how to build it. Here's the trigger that's breaking the limit:
Code:
for (Integer i = 0; i < Trigger.new.size(); i++) { Contact p = [SELECT Phone FROM Contact WHERE contact.id = :ContactID limit 1]; Contact E = [SELECT Email FROM Contact WHERE contact.id = :ContactID limit 1]; Trigger.new[i].Contact_Phone__C = p.Phone; Trigger.new[i].Contact_Email__C = e.Email; }
Help Suggestions greatly appreciative. If anyone has a good resources that gives me examples of similar triggers that would be fantastic learning tool for me. Thank you!
You need to make your code handle bulk actions.
Here is a post that might help, post.
The goal is to do as few queries as possible and call variables in memory to get the results when you need them.
Also and this is a rule of thumb with Apex development, never put a query in a for loop.
Hope this helps.
Then test to see if the values of his phone and email are correct.
There is a section on testMethods in the Apex docs that is very helpful.
I've been through it twenty times and maybe I'm stupid.
Can you create this test class that works with this example and post it. I'll try to figure it out.
But I'm out of time. My trigger works and I HAVE to deploy it. I'm going to my superiors and telling them that SalesForce is not going to do what we need if I cannot deploy my trigger.
Thank you so much.
Please example the class.
Mikef:
Thanks for the guidance. I've been pouring over that very same post and trying to make it applicable to my specific trigger.
Let me sum up what I think that trigger doing and what I would do for my example.
I would create a set of all Contact ids in the trigger.
Then I would create a map<string,id> using a SQoL Query for all IDs in the Contact Set created above
Then I do a for loop for each Contact Id in the Trigger set and within the for loop is where the Product Contact Role contact_Phone will get updated.
Is this correct?
That is correct you have the logical steps down, and those are the steps for 80% of what you want to do in Apex.
Get a set of values to query
Query to populate a Map
Call the map to get your new vales.
So here is how I would write what you need todo.
I am sorry to hear that you have to tell your boss that salesforce can't do what you need todo.
I am sure you figured it out but this testMethod is the one thing keeping you back.
So let's try to solve that, I think we can do it.
One thing writing the test class would take me a long time so I can't do all the work for you but I can provide you with a frame work.
Will that help?
Well I ended up writing the whole thing.
That should give Brian's trigger 100% code coverage.
Mikef - Thanks. Your first post was enough guidance that I figured it out and got it to work. The only thing I did different was I created two maps, one for the e-mail and one for the phone.
Is there any benefits to creating a single map other than it's 1 less away from the gov. limit?
The benefit for one map is cleaner code, and less resources used up.
Plus one could say it's "faster" but we are dealing in a few milliseconds so not sure a user would care.
What is your thinking for using two maps?
Does it make more sense to you?
Mike,
The reason I used two maps was simply for my own Sanity. I wanted to make sure I didn't forget anything.
Plus, I originally used 1 map just for the phone. Once I got to work I just cloned the code to add the e-mail. By not changing the phone I was ensured that I didn't "Break" the code to complete it. A benefit for me is if it Errors it'll tell me if it Errored on the Phone or the e-mail. Matter of fact when I did my dataloader I did get an error on about 7% of the items due to Null References in the e-mail.
I agree doing it in 1 map is cleaner and will probably be how I will code it in the future.
Thank you again for your help!
To get rid of the null errors you have to check to see if the map was populated before you try and update the record.
The email will be blank which is fine cause there isn't an email on the contact record if the map is null.
Same for Phone.
trigger MyTrigger on Account (before insert, before update) {
Set<Id> accId = new Set<Id>();
for (Account a : Trigger.new){ accId.add(a.Id);}
Map<Id,Account> accMap = new Map<Id,Account>([select Id, AField__c from Account where Id in : accId]);
Contact c = [select Id from Contact where CField__c = :
accMap.get(accId).AField__c];for(Account a : Trigger.new){a.Contact__c = c.id; }
}
Clearly the struckthrough reference is incorrect, but what does need to go there?
Thanks
The logic that you are using in your trigger is not correct.
You are doing something like this:
- on an account trigger get all the account ids
- Query the account table for a list of accounts that match your Ids
- query the contact table for the same records that have matching custom fields
- update the account with a custom contact lookup with the contacts that come back
There are a few things wrong with this logic.- you're querying on the account trigger for a list of accounts in the trigger. (you have the list you need cause you have the trigger)
- you're not using the standard relationship that accounts have with contacts.
So I have a question for you.Can you explain, in plain english, what you are trying to do?
I think I know but I don't want to suggest a solution on a assumption.
Mike,
Sorry for the confusion.
I would like to design a bulk trigger to find which Contact record has CField__c = AField__c from Account and assign the Contact Id to Contact__c on the Account.
Mike,
I took a stab.
Does this make more sense:
trigger MyTrigger2 on Account (before insert, before update) {
Set<Id> accId = new Set<Id>();
for (Account a : Trigger.new){accId.add(a.Id);}
Contact c = ([select Id, CField__c, Account.Id from Contact where Account.Id in : accId]);
for(Account a : Trigger.new){
if(a.AField__c == c.CField__c){
a.Contact__c = c.id;}
}}
I am not sure you want this to run on insert. I can't think of a use case where a someone or something can insert an account and contacts at the same time. You can insert an account get the Id back and insert related contacts. But not all in the same API transaction.
That should do it, but the issue you're going to have is you may have many contacts for that account and you really want to check all contacts under the account for the match.
So you might want to you List the query and search the List for all the contacts with that AccountId.