You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Salesforce SOQL 101报错:沙箱正常生产异常,求代码优化方案

问题根因

你的代码触发SOQL 101限制的核心原因是SOQL查询直接写在Notice__c记录的遍历循环内部:

  • 每遍历1条Notice__c记录,就会执行2次SOQL(一次查CampaignMember,一次查ContentDistribution)
  • 沙箱测试时Notice数据量少,不会触发上限,生产环境数据量超过50条就会突破单事务101次SOQL的限制
  • 你之前尝试的批量写法未生效,是因为没有把查询逻辑移到循环外部,查询操作依然是每次循环重复执行
优化方案

严格遵循Salesforce Apex bulkify规范,所有查询操作全部移到循环外部,全程只执行3次SOQL即可完成所有数据组装:

  1. 先查询所有Notice__c记录,遍历收集全量的关联ID(OfficialSenders__c即CampaignID、ContentDocumentId)
  2. 用收集到的全量ID集合,分别执行1次CampaignMember查询、1次ContentDistribution查询,将结果按关联ID分组存储为Map结构
  3. 再次遍历Notice__c记录,直接从预查询的Map中匹配对应关联数据,组装Wrapper对象
优化后核心代码示例(getNotice方法)
@HttpGet(UrlMapping='/API/V1/notice/all')
global static List<String> getNotice(){          
    List<Object> senderJson = new List<Object>();  
    // 第一步:查询所有Notice记录,收集全量关联ID
    List<Notice__c> noticeList = [SELECT Name, ClosingDate__c,Contents__c, NoticeTypes__c,createddate,OfficialSenders__c,id,(SELECT ContentDocumentId  FROM ContentDocumentLinks) FROM Notice__c];
    Set<Id> allCampaignIdSet = new Set<Id>();
    Set<Id> allContentDocIdSet = new Set<Id>();
    for(Notice__c a : noticeList){
        if(a.OfficialSenders__c != null) allCampaignIdSet.add(a.OfficialSenders__c);
        for(ContentDocumentLink cdl: a.ContentDocumentLinks){
            if(cdl.ContentDocumentId!=null) allContentDocIdSet.add(cdl.ContentDocumentId);
        }
    }

    // 第二步:批量查询所有关联数据,仅执行2次SOQL
    // 按CampaignId分组存储对应的AccountId列表
    Map<Id, List<Id>> campaign2AccountIdsMap = new Map<Id, List<Id>>();
    if(!allCampaignIdSet.isEmpty()){
        for(CampaignMember cm : [select accountid ,CampaignId from CampaignMember where CampaignId IN: allCampaignIdSet]){
            if(!campaign2AccountIdsMap.containsKey(cm.CampaignId)){
                campaign2AccountIdsMap.put(cm.CampaignId, new List<Id>());
            }
            campaign2AccountIdsMap.get(cm.CampaignId).add(cm.AccountId);
        }
    }
    // 按ContentDocumentId分组存储对应的公共链接列表
    Map<Id, List<String>> contentDoc2UrlMap = new Map<Id, List<String>>();
    if(!allContentDocIdSet.isEmpty()){
        for(ContentDistribution cdb : [select distributionPublicURL,ContentDocumentId from contentDistribution where ContentDocumentId IN: allContentDocIdSet]){
            if(!contentDoc2UrlMap.containsKey(cdb.ContentDocumentId)){
                contentDoc2UrlMap.put(cdb.ContentDocumentId, new List<String>());
            }
            contentDoc2UrlMap.get(cdb.ContentDocumentId).add(cdb.DistributionPublicUrl);
        }
    }

    // 第三步:组装Wrapper,无任何SOQL操作
    for (Notice__c a: noticeList) {               
        NoticeWrapper nw = new NoticeWrapper();
        nw.noticeid = a.Id;               
        nw.ClosingDate = a.ClosingDate__c;                  
        nw.NoticeTypes = a.NoticeTypes__c;                
        nw.Contents = a.Contents__c;            
        nw.Name = a.Name;         
        nw.createddate = a.createddate; 
        // 匹配关联的AccountId列表
        if(campaign2AccountIdsMap.containsKey(a.OfficialSenders__c)){
            nw.accountId = campaign2AccountIdsMap.get(a.OfficialSenders__c);
        }
        // 匹配关联的公共链接列表
        List<String> urls = new List<String>();
        for(ContentDocumentLink cdl: a.ContentDocumentLinks){
            if(cdl.ContentDocumentId!=null && contentDoc2UrlMap.containsKey(cdl.ContentDocumentId)){
                urls.addAll(contentDoc2UrlMap.get(cdl.ContentDocumentId));
            }
        }
        if(!urls.isEmpty()) nw.DistributionPublicUrl = urls;
        senderJson.add(nw);    
    }

    List<String> sends = new List<String>();
    for(Object json : senderJson){
        sends.add(String.valueof(json));
    }
    return sends;    
}
其他优化建议
  • getOneNotice方法因为仅查询单条Notice记录,本身不会触发SOQL上限,也可以参照上述逻辑清理冗余代码
  • 可根据业务需求给Notice__c的查询增加分页/时间范围过滤,避免单次查询返回过多数据触发其他 governor limit

内容的提问来源于stack exchange,提问作者Dang it

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 10:15:02