Salesforce SOQL 101报错:沙箱正常生产异常,求代码优化方案
问题根因
你的代码触发SOQL 101限制的核心原因是SOQL查询直接写在Notice__c记录的遍历循环内部:
- 每遍历1条Notice__c记录,就会执行2次SOQL(一次查CampaignMember,一次查ContentDistribution)
- 沙箱测试时Notice数据量少,不会触发上限,生产环境数据量超过50条就会突破单事务101次SOQL的限制
- 你之前尝试的批量写法未生效,是因为没有把查询逻辑移到循环外部,查询操作依然是每次循环重复执行
优化方案
严格遵循Salesforce Apex bulkify规范,所有查询操作全部移到循环外部,全程只执行3次SOQL即可完成所有数据组装:
- 先查询所有Notice__c记录,遍历收集全量的关联ID(OfficialSenders__c即CampaignID、ContentDocumentId)
- 用收集到的全量ID集合,分别执行1次CampaignMember查询、1次ContentDistribution查询,将结果按关联ID分组存储为Map结构
- 再次遍历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
相关产品推荐
相关产品推荐

