1

I am having an issue because I believe I don't understand batch apex/scheduling very well. Every time I use this I get either a SOQL error of trying to query too many rows of 50001, or a DML error trying to add too many things in the list to update. Any help would be greatly appreciated

The code

global class LastMktoSyncDate implements Database.Batchable<sObject>{


global List<sObject> start(Database.BatchableContext c)
{
    date d = system.today().addDays(-7);
    List<sObject> scope = new List<sObject>();

    scope.addAll([SELECT id, Name, Sync_to_mkto__c, account_name__c from Contact where ((Last_Marketo_Sync_Date__c = NULL OR Last_Marketo_Sync_Date__c < :d) AND Sync_To_Mkto__c != True)]);

    scope.addAll([SELECT id, Name, Sync_to_mkto__c from Lead where ((Last_Marketo_Sync_Date__c = NULL OR Last_Marketo_Sync_Date__c < :d) AND Sync_To_Mkto__c != True)]);

    return scope;
}

global void execute(Database.BatchableContext c, List<sObject> scope)
{
    List<Contact> con_toupdate = new List<Contact>();

    List<Lead> lead_toupdate = new List<Lead>(); 

    for(sObject obj : scope)
    {
        switch on obj
        {
            when Contact con
            {
                con.Last_Marketo_Sync_Date__c = system.today();
                con_toupdate.add(con);
            }
            when Lead lea
            {
                lea.Last_Marketo_Sync_Date__c = system.today();
                lead_toupdate.add(lea);
            }
         }
     }

    update con_toupdate;
    update lead_toupdate;
 }

 global void finish(Database.BatchableContext c)
 {
 }
}

The Scheduler

global class ScheduledMktoSync implements Schedulable{
    global void execute(SchedulableContext sc){
        LastMktoSyncDate l = new LastMktoSyncDate();
        database.executeBatch(l);
    }
}
5

When you do addAll you're still subject to the Apex Governor limits for a single transaction. That is, you're getting too many records and the list cannot iterate.

If you want to iterate on more records, you probably want to use a QueryLocator instead. Since you have two different queries, in your case that probably means that you're going to need two batch jobs (maybe you can chain them?)

An example of using a queryLocator follows:

public Database.QueryLocator start(Database.BatchableContext BC) {
    String query = 'SELECT Id FROM MyObject__c';

    return Database.getQueryLocator(query);
}

The rest of your code (execute and finish) remains unchanged other than making sure you're only processing records that match the querylocator you sent (contacts OR leads)

UPDATE:

A fun way of doing this with minimal repetition is to have a constructor in your class and then returning two different locators depending on a parameter.

For example:

String myQuery;

global LastMktoSyncDate(String objectName) {
    switch on objectName {
        when 'Contact' {
           myQuery = 'your query for contacts';
        }
        when 'Lead' {
           myQuery = 'your query for lead';
        }
    }
}

public Database.QueryLocator start(Database.BatchableContext BC) {

    return Database.getQueryLocator(myQuery);
}
  • 1
    Thank you. This worked. I was trying to be cheeky and not write a different class – adam Jun 14 at 17:20
  • Well, if you implement the suggestion you may not have to. :) Just call the same class with a different constructor on the finish method – Sebastian Kessel Jun 14 at 17:40

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.