+ Start a Discussion
RDN_LHRRDN_LHR 

Any suggestions for when governor limits make your triggers useless??

Hi,

 

I'm getting frustrated with governor limits on triggers.  I've taken all the correct steps as per the documentation (except the "helper class" one) and it's not a case anymore of coding correctly.  It's now just a case of having a lot of data in the system.

 

In the "Force.com Cookbook" the chapter called "Avoiding Apex Governor Limits" has a beautiful example that I would like to use almost exactly as it is.  In the example, whenever an address changes on an account, a trigger updates the address onto all the contacts.

 

Now this is wonderful if you don't have too many contacts, if you only sell to small companies.  If you sell to large companies as we do and we have hundreds of sales and service contacts in a company, such a trigger will most likely hit governor limits no matter what you do.

 

What good would such a trigger be if it could only update, say, the first 100 contacts out of 500 and leave the other 400 unmodified?

 

What other magic tricks have you guys done when you've found yourself in similar situations?  Do you just say, oh well, triggers can't help us or are there some really clever ways I've not thought of?

 

I hope I can get a couple of clever tips from the experts. 

Best Answer chosen by Admin (Salesforce Developers) 
werewolfwerewolf
Try moving some work to an @future method.  See here, for example (although in that blog entry I used @future to do some work I couldn't do in a trigger, not to get around limits, but in general the limits on @future methods are much more relaxed than in the trigger).

All Answers

RDN_LHRRDN_LHR

Here's another nice simple example, besides the one in the Force.com Cookbook that I referred to previously. 

 

You'll notice I've stuck in the "limit 99" just to make it work.  Now if in case a Contact has more than 100 Subscriptions, the trigger won't error, but it will leave any Subscriptions after the first 99 untouched -- until such time as the trigger fires again for that Contact, at which time it will grab the next 99.

 

 

trigger updateSubs on Contact (after insert, after update) { map <Double,Id> contactMap = new map <Double,Id>(); for (Integer i = 0; i < Trigger.new.size(); i++) { if (Trigger.old[i].Star_Contact_Id__c != Trigger.new[i].Star_Contact_Id__c && Trigger.new[i].Star_Contact_Id__c !=null) { contactMap.put(Trigger.new[i].Star_Contact_Id__c,Trigger.new[i].Id); } } list <Subscription__c> updateSubsList = new list <Subscription__c>(); for (Subscription__c sub: [select Id, Contact__c, Star_Contact_Id__c from Subscription__c where Contact__c = null and Star_Contact_Id__c in :contactMap.keySet() limit 99 ]) { sub.Contact__c = contactMap.get(sub.Star_Contact_Id__c); updateSubsList.add(sub); } update updateSubsList; }

 

Message Edited by RDN_LHR on 04-16-2009 03:14 AM
werewolfwerewolf
Try moving some work to an @future method.  See here, for example (although in that blog entry I used @future to do some work I couldn't do in a trigger, not to get around limits, but in general the limits on @future methods are much more relaxed than in the trigger).
This was selected as the best answer
RDN_LHRRDN_LHR
I think I'm really close, but with one small problem.  I've changed the last line of the above trigger to

"massUpdateSubs.updateSubs(updateSubsList);"  then I wrote this very simple class:

 

public class massUpdateSubs { // @future public static void updateSubs (List<Subscription__c> subscriptions) { update subscriptions; } }


It works with @future commented out -- doing the exact same thing as the trigger with the same governor limit as you'd expect for now.

 

When I uncomment the @future and save the class I get this error:

 

Save error: Unsupported parameter type LIST:SOBJECT:Subscription__c

 

 

UmapenUmapen

Hello RDN_LHR,

I am having exact same problem. were you able to find the solutions to this limit?

 

 

Thanks

werewolfwerewolf

You can't pass Sobjects to an @future method because the Sobjects you're referring to may have changed by the time the @future runs, but you can pass a List<Id>.  So instead of passing the List<Subscription__c>, pass a List<Id> and then, as your first line in the @future, do something like

 

List<Subscription__c> subs = [SELECT <fields you need> FROM Subscription__c WHERE Id IN :idList];

MetaWhatMetaWhat
One thing to remember when using the @future is that it is no longer in  the same unit of work.   Be careful if you are moving logic that really should stay within one unit of work.
RDN_LHRRDN_LHR

Yes I've solved it as recommended.  My trigger passes a set of Id's to this helper class with @future and most of the work that used to be in the trigger is now in this class. 

 

The trigger could only do 100 rows before throwing an error.  Now in this method, I've tested with 500 rows and it worked.  :smileyhappy:

 

public class massUpdateSubs { @future public static void updateSubs (Set<Id> contacts) { map <Double,Id> contactMap = new map<Double,Id>(); for (Contact c: [select Star_Contact_Id__c,Id from Contact where Id in :contacts]) { contactMap.put(c.Star_Contact_Id__c,c.Id); } list <Subscription__c> updateSubsList = new list <Subscription__c>(); for (Subscription__c sub: [select Id, Contact__c, Star_Contact_Id__c from Subscription__c where Contact__c = null and Star_Contact_Id__c n :contactMap.keySet()]) { sub.Contact__c = contactMap.get(sub.Star_Contact_Id__c); updateSubsList.add(sub); } update updateSubsList; } }

 

UmapenUmapen

How can I optimize this following code. Line in red is giving me error 

public class createMycustomAcc {

   @future
  public static void Acctsetsfor(String[] acIds){
   List<customobject__c> finalLst new List<customobject__c>()
 
     List<Account> ac = [select Id, Name, Market_name__c from Account where market_name__c != null AND Id IN :acIDs];

 for(Account a :ac){
     String RecType = a.RecordTypeId;
     String Rating = a.Rating__c;
     String SameId = a.Id;
     String MarketName = a.market_name__c;


     qrystr= 'select name, Id, Rating__c from Account WHERE RecordTypeId = \'' + RecType + '\' AND Id != \'' + SameId +'\' AND market_name__c = \'' + marketName + '\'  custom_id__c != Null LIMIT 150';
      Htlaccts = Database.query(qrystr);  // this line is causing errors
    
     if(Htlaccts.size() >0{
       for((Integer i = 0; i < Htlaccts.size();i++){
          // more conditions if true
          //build a list of custom object
          // if false i = Htlaccts.size()
          customobject__c cobj = new customobject__c(Account__c = a.id, relatedto__c =Htlaccts[i].id);
          
          finalLst.add(cobj);
       }

     }
     if(finalLst.size() > 0 ) insert finalLst;


  }

 

JimRaeJimRae

you didn't specify what error you were getting, but I would assume the problem is that you don't have Htlaccts defined anywhere.

At a minimum, it should be something like this:

 

 

List<Account> Htlaccts = new List<Account>(Database.query(qrystr));

 

 

 

UmapenUmapen

I am getting  Too many SOQL queries: 101 error

 

I defined my list as List<Account> Htlaccts = new List<Account> ();  at the beginning of the method line 3 just forgot to in my posting.

If I change as per your suggestion,  the query is still in for loop

 

for(Account a :ac){   List<Account> Htlaccts = new List<Account>(Database.query(qrystr));}

  Is that going to work I will try?

 

 

JimRaeJimRae

no, that will not work.  You need to refactor your code to get that query outside of the loop.

The best model to use would be to capture all of the potential results in a map and then reference the map in your loop.  Search for references to Bulk trigger, there is lots of sample code out there.

UmapenUmapen

I did something like this and still got the error because it is in for loop

 

map <Id, Account[]> htlaccts = new map<ID, Account[]>()
for(Account eachAcct:ac){
htlaccts.put(eachaccount.id, Database.query(qrystr))

}


   

werewolfwerewolf

Well, like Jim said:

 

You need to refactor your code to get that query outside of the loop.

RDN_LHRRDN_LHR

Umapen,

 

Look at my final example further up in this posting.  I think it does what the guys are suggesting for you to do.

 

  • On line 3, I create the map.

 

  • On line 4, I do the "for" loop.  Notice where the SOQL query is -- not inside the curly brackets.

 

  • On line 5, I put the values into the map by looping over the query results.

 

 

It took me a lot of frustrating practice and a lot of reading posts and making a lot of mistakes before I finally got the concept in my head.  Even now, I think that maybe I've got it right.  I'm still never 100% sure.

 

 

Message Edited by RDN_LHR on 04-20-2009 09:47 AM