如何从 for 循环中获取 SOQL 查询

How to get a SOQL query out of a for loop

已添加代码。

我在这里无休止地寻找解决方案,但找不到解决方案,请帮忙!

我有三个对象(A、B 和 C)。 A 有对 B 的查找,B 是 C 的主人(详细信息)。 A 和 C 都有很多记录与每个 B 记录相关。

我想要一份工作 运行 从对象 C 获取记录的子集(通常大约有 5,000 条记录)。然后遍历每一个并获取对象 A 上查找相同对象 B 记录的记录,总结一个对象 A 数字字段,并将其放在 C 记录上。

我已经成功地让它在小范围内工作,<100 个对象 C 记录。但是每个 Object C 记录都需要一个新的 SOQL 查询,因为我在获取所有 Object C 记录后在 for 循环中迭代它们。另外,我知道在循环中进行查询并不是最佳做法。

我怎样才能让它工作?由于记录与对象 B 共享关系,是否有另一种方法可以从匹配的对象 A 记录中获取数据?或者有什么方法可以提取两个列表,一个对象 C 和一个对象 A。然后汇总对象 A 记录并将列表排列成某种方式?

提前致谢!

代码:

public class nightlyJob {
    public static void updateNumbers(){
        integer I = 29; 
        List<ObjectC__c> CUpdateList = new List<ObjectC__c>();
        List<ObjectC__c> CpullList = 
                  [SELECT ID, Index__c, ObjectB__r.id
                  FROM ObjectC__c
                  WHERE Index__c = :I];
                for(ObjectC__c s : CpullList){
                        List<ObjectA__c> AList =
                            [SELECT ObjectB__c, Number__c
                            FROM ObjectA__c
                            WHERE ObjectB__c = :s.ObjectB__r.Id];
                    decimal NumSum = 0;
                    for(ObjectA__c a : AList){
                        NumSum = a.Number__c + NumSum;
                    }
                    s.Num__c = NumSum;
                    CUpdateList.add(s);
                }
        update CUpdateList;
    }            
}

看来您目前确实缺少几个基本概念。

您在 SFDC 开发中遇到的最大问题是 "database" 操作非常昂贵并且受到严格限制。这不仅仅是 "best practice" 的问题:如果在单个事务中超过这些限制——SOQL 调用次数、返回记录数、更新记录数、DML 语句数等——你的事务将失败。有关详细信息,请在线搜索 "Salesforce Execution Governors and Limits"。

您可以编写在这些限制范围内工作的代码,但有一点学习曲线。

首先,学习将集合与 SOQL 查询结合使用,以使您的 SOQL 查询脱离循环。这是a.k.a。 "bulkfication" 它是 SFDC 开发的基础:

    List<ObjectC__c> CpullList = 
              [SELECT ID, Index__c, ObjectB__r.id
              FROM ObjectC__c
              WHERE Index__c = :I];
    // Create a map with the results of this query.  
    // key=ObjectC__c.Id, value = Object__c record
    Map<Id, ObjectC__c> objCmap = Map<Id, ObjectC__c>(CpullList);

    // Build a set of all the Object_B id's from this result set
    Set<Id> objBids = new Set<Id>();
    for (ObjectC__c record : CpullList) {
        objBids.add(record.ObjectB__r.id);
    }

    // Now you can use only one SOQL query instead of a loop
    List<ObjectA> AList = [SELECT ObjectB__c, Number__c
                        FROM ObjectA__c
                        WHERE ObjectB__c in:objBids];

接下来,尽可能使用 "SOQL aggregate functions"。示例:在此处的代码中,您可以使用 "SUM()" 和 "group by" 而不是使用循环执行这些计算:

    // Get the sum of ObjectA__c.Number__c for each Object B in objBIds
    AggregateResult[] groupedResults = [select ObjectB__c, 
                                        sum(Number__c) sumA 
                                        from ObjectA__c 
                                        where ObjectB__c in: objBids 
                                        group by ObjectB__c];

    for (AggregateResult ar : groupedResults)  {
        System.debug('Object B Id' + ar.get('Objectb__c'));
        System.debug('Sum of ObjectA__c.Number__c' + ar.get('sumA'));

        // Here, you might want to build a Map<Id, Integer> sumAmap:
        // key=Object B ID, value=sumA
        // and then use it along with objCmap to build a collection of Object C's 
        // for your update statement...
    }

您可以继续这个过程并应用这些想法来提高代码的效率。

但即使您的方法尽可能高效地工作,您仍然可能 运行 由于要处理的记录数量而受到限制。到那时,您将需要了解 Batchable 接口、Queuable 接口和@future 调用(如何处理大量记录,跨事务拆分)这些信息实在太多,无法在单个 SO 答案中涵盖。