Apexバッチで集計関数が使えない

さくっと質問の回答をつくろうとしたら、エラーです。

Aggregate query does not support queryMore(), use LIMIT to restrict the results to a single batch

何で？えええええ。
調べたら解決策がありました。

https://salesforce.oikeru.com/entry/BatchApex


#質問
https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000BdRF6SAN

#見直したプログラム
やっぱり、普通にアカウントから攻めた。

```
public class BAT_TowContactsOneAccount2 implements Database.Batchable<sObject> , Database.Stateful {
    
    private Integer executeCount = 0;  
    private Integer Count = 0;
    
    public BAT_TowContactsOneAccount2 () {
    }
    
    public Database.QueryLocator start(Database.BatchableContext BC) {
      
        return Database.getQueryLocator(getTargetQuery());
    }
    
    public void execute(Database.BatchableContext BC, List<Account> scope) {
        
        Savepoint sp = Database.setSavepoint();
        try {
            Count += scope.Size();
            executeCount++;
            
            List<Contact> insertContactList = new List<Contact>();
            system.debug(Logginglevel.INFO,'====scope===========>>> '+ scope );  
            for (Account Acc : scope) {
                system.debug(Logginglevel.INFO,'====Acc.Contacts.size()===========>>> '+ Acc.Contacts.size() );  
                If (Acc.Contacts.size() == 0) {
                    Contact c1 = new Contact();
                    c1.LastName = 'test2-1';
                    c1.FirstName = 'xx';
                    c1.AccountId = Acc.Id;
                    insertContactList.add(c1);
                    
                    Contact c2 = new Contact();
                    c2.LastName = 'test2-2';
                    c2.FirstName = 'xx';
                    c2.AccountId = Acc.Id;
                    insertContactList.add(c2);
                }
                
                If (Acc.Contacts.size() == 1) {
                    Contact c1 = new Contact();
                    c1.LastName = 'test2-1';
                    c1.FirstName = 'xx';
                    c1.AccountId = Acc.Id;
                    insertContactList.add(c1);
                    
                }
                
                
            }
            if (insertContactList.size() >0) insert insertContactList;        
            
        }catch(Exception e) {
            Database.rollback(sp);           
        } finally {
        }
    }
    
    public void finish(Database.BatchableContext BC) {
        System.debug('******** BAT_TowContactsOneAccount finish executeCount=' + executeCount + ', Count=' + Count);
        
    }
    
    private String getTargetQuery() {    	
        String query = 'SELECT Id,(SELECT Id FROM Contacts) FROM Account ';
        //system.debug(Logginglevel.INFO,'================>>>query '+ query );     
        return query;
    }

}
```


#最初のプログラム
考え方:コンタクトの数を数えて、2より小さいもを対象にすればいいかな？
撃沈!!!

よく考えたらコンタクトが無いアカウントが抽出できん。あたりまえだな。

```
public class BAT_TowContactsOneAccount implements Database.Batchable<AggregateResult> , Database.Stateful {

    private Integer executeCount = 0;  
    private Integer Count = 0;
    
    public BAT_TowContactsOneAccount () {
    }
    
    public Iterable<AggregateResult> start(Database.BatchableContext BC){
        System.debug('******** BAT_TowContactsOneAccount start');
        String query = 'SELECT Count(Id),AccountId FROM Contact Group by AccountId HAVING COUNT(Id) < 2';
        return new SearchResultIterable(query);
    } 
    
    //public Database.QueryLocator start(Database.BatchableContext BC) {
    //   
    //    return Database.getQueryLocator(getTargetQuery());
    //}    
    
    
    public void execute(Database.BatchableContext BC, List<AggregateResult> scope) {
        
        Savepoint sp = Database.setSavepoint();
        try {
            Count += scope.Size();
            executeCount++;
            
            List<Contact> insertContactList = new List<Contact>();
            system.debug(Logginglevel.INFO,'====scope===========>>> '+ scope );  
            for (AggregateResult AR : scope) {
                If (AR.get('expr0') ==0) {
                    Contact c1 = new Contact();
                    c1.LastName = 'test2-1';
                    c1.FirstName = 'xx';
                    c1.AccountId = (Id)AR.get('AccountId');
                    insertContactList.add(c1);
                    
                    Contact c2 = new Contact();
                    c2.LastName = 'test2-2';
                    c2.FirstName = 'xx';
                    c2.AccountId = (Id)AR.get('AccountId');
                    insertContactList.add(c2);
                }
                
                If (AR.get('expr0') ==1) {
                    Contact c1 = new Contact();
                    c1.LastName = 'test1';
                    c1.FirstName = 'xx';
                    c1.AccountId = (Id)AR.get('AccountId');
                    insertContactList.add(c1);
                }
            }
            if (insertContactList.size() >0) insert insertContactList;        
            
        }catch(Exception e) {
            Database.rollback(sp);           
        } finally {
        }
    }
    
    public void finish(Database.BatchableContext BC) {
        System.debug('******** BAT_TowContactsOneAccount finish executeCount=' + executeCount + ', Count=' + Count);
        
        
    }
    
    //private String getTargetQuery() {    	
    //    String query = 'SELECT Count(Id),AccountId FROM Contact Group by AccountId HAVING COUNT(Id) < 2 ';
    //    //system.debug(Logginglevel.INFO,'================>>>query '+ query );     
    //    return query;
    //}
    
    public class SearchResultIterator Implements Iterator<AggregateResult>{
        public AggregateResult [] results {get;set;}
        public Integer index {get;set;}
        
        public SearchResultIterator(String query){
            this.index = 0;
            results = Database.query(query);
        }
        
        public boolean hasNext(){
            return results !=null && !results.isEmpty() && index < results.size();
        }
        
        public AggregateResult next(){
            return results[index++];
        }
    }
    
    public class SearchResultIterable implements Iterable<AggregateResult>{
        private String query;
        
        public SearchResultIterable(String soql){
            this.query = soql;
        }
        
        public Iterator<AggregateResult> Iterator(){
            return new SearchResultIterator(this.query);
        }
    }
}
```