[SalesForce] Apex CPU Time limit exceeded error

I have a batch apex class that is facing CPU time limit exceeded error. Can someone please help me to by pass this error here? Please find my batch apex here.

global class Account_chk_batch implements Database.Batchable<sObject>{
  public String query;
    global Database.QueryLocator start(Database.BatchableContext BC){
    List<Attachment> exist=new List<Attachment> ();
    exist=[select ParentId,id from Attachment];
    Map<Id, Attachment> existMap = new Map<Id, Attachment>();
    for (Attachment att : exist) {
    existMap.put(att.ParentId, att);
}      
set<Id> keys = existMap.keySet();
      query = 'select Id,(select Name from Call2_vod__r where Signature_Date_vod__c !=Null and Account_vod__c not in :keys limit 1) from Account';
      return Database.getQueryLocator(query);
 }
  global void execute(Database.BatchableContext BC, List<sObject> scope) {      
      List<Call2_vod__c> a= new List<Call2_vod__c>();      
      List<Attachment> p=new List<Attachment> ();

Map<Id,Call2_vod__c> signMap = new Map<Id, Call2_vod__c>();          
for(sObject s : scope){Account call = (Account)s;
    //for (Account call: [select Id,(select Name from Call2_vod__r where Signature_Date_vod__c !=Null and Account_vod__c not in :existMap.keySet() limit 1) from Account ])

      for(Call2_vod__c c : call.Call2_vod__r){
        if(call.Name== c.Name){

      Attachment record = new Attachment(Name = Call.Name ,ParentId = call.Id, Body = EncodingUtil.base64Decode(c.Signature_vod__c), ContentType = 'image/jpeg');
       if (record.parentId !=Null ) {
        p.add(record);
}
}                      
    }
    try { insert(p);
    } catch(System.DMLException e){
      System.debug('The following exception has occurred: ' + e.getMessage());
      }
  }
}
global void finish(Database.BatchableContext BC){
   }
}

Best Answer

The only work done in the start method should be the creation of the query locator and that should ideally not include related (child) records because the numbers of those cannot be controlled by the batching mechanism.

So some major refactoring is needed to something like this (which is untested and may or may not be logically correct so review line by line):

public class Account_chk_batch implements Database.Batchable<sObject>{

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

        return Database.getQueryLocator([
                select Id, Name, Account_vod__c, Signature_vod__c
                from Call2_vod__c
                where Signature_Date_vod__c != Null
                and Account_vod__c != null
                and Signature_vod__c != null
                ]);
    }

    public void execute(Database.BatchableContext bc, List<sObject> scope) {

        Set<Id> parentIds = new Set<Id>();
        for(sObject sob : scope) {
            Call2_vod__c call = (Call2_vod__c) sob;
            parentIds.add(call.Account_vod__c);
        }

        // Remove ids that already have an Attachment
        for (Attachment a : [
                select ParentId
                from Attachment
                where ParentId in :parentIds
                ]) {
            parentIds.remove(a.ParentId);
        }

        if (parentIds.size() > 0) {
            List<Attachment> inserts = new List<Attachment>();
            for(sObject sob : scope) {
                Call2_vod__c call = (Call2_vod__c) sob;
                if (parentIds.contains(call.Account_vod__c)) {
                    inserts.add(new Attachment(
                            Name = call.Name,
                            ParentId = call.Id,
                            Body = EncodingUtil.base64Decode(call.Signature_vod__c),
                            ContentType = 'image/jpeg'
                            ));
                }
            }
            insert inserts;
        }
    }

    public void finish(Database.BatchableContext bc) {
    }
}