本文已参与「新人创作礼」活动,一起开启掘金创作之路
背景介绍
在项目的demo期,为了快速完成任务,有时候难免在项目上写出一些性能极差的代码,比如hard code(没有遵循可配置化的思想实施),再比如在for循环里面做查询。本节我们将针对在for循环里面做查询这个主题做优化分享。
要点概要
Q1:什么是query 101错误?
A1:通俗的讲,就是查询次数太多超出了单个事务中API限制。
Q2:有代码实例来再现这个问题吗?
A2:以促销为例展开说明。
Demo介绍
假设我们有一个促销,促销指对某一类产品,我们设置一个促销规则,然后推送给需要care的用户。这时,我们设计了促销,促销详细,促销产品这三个master-detail的关系对象。
促销 - 用来记录促销起止日期,促销的DM单,促销的状态和发布情况;
促销详细 - 记录某类产品的促销最小数量,折扣;
促销产品 - 是根据促销详细中产品类型过滤出来的产品中选择的具体产品项。
Demo目标
为了避免选择的促销详细重复,我们需要在促销详细新建时提示已重复的促销详细所选的产品类型 - 用Trigger实现。
代码示例
优化前的Code展示
public class ACC_PromotionDetailTriggerHandler extends ACC_TriggerHandler {
public override void beforeInsert() {
for(ACC_Promotion_Detail__c pd : (List<ACC_Promotion_Detail__c>)Trigger.New) {
Set<String> productTypeSet = new Set<String>();
List<ACC_Promotion_Detail__c> pdList = new List<ACC_Promotion_Detail__c>();
pdList = [SELECT ACC_Product_Group__c, ACC_Product_Subgroup__c FROM ACC_Promotion_Detail__c WHERE ACC_Promotion__c = :pd.ACC_Promotion__c];
for(ACC_Promotion_Detail__c pd1 : pdList) {
productTypeSet.add(pd1.ACC_Product_Group__c + pd1.ACC_Product_Subgroup__c);
}
if(productTypeSet.size() > 0 && String.isNotBlank(pd.ACC_Product_Group__c) && String.isNotBlank(pd.ACC_Product_Subgroup__c)) {
String productType = pd.ACC_Product_Group__c + pd.ACC_Product_Subgroup__c;
if(productTypeSet.contains(productType)) {
pd.addError(Label.ACC_Promotion_Detail_Validation_Not_Duplicated);
}
}
}
}
}
分析:我们可以看出上述代码有两层遍历,之间加了一个select查询,如果数据量大的话,就可能出现query 101异常。
优化后的Code展示
public class ACC_PromotionDetailTriggerHandler extends ACC_TriggerHandler {
public override void beforeInsert() {
Set<Id> promotionIdSet = new Set<Id>();
for(ACC_Promotion_Detail__c pd : ((List<ACC_Promotion_Detail__c>)Trigger.New)) {
promotionIdSet.add(pd.ACC_Promotion__c);
}
if(promotionIdSet.size() > 0) {
ACC_PromotionDetailTriggerFunction.duplicatePDIValidation(promotionIdSet, Trigger.New);
}
}
}
public class ACC_PromotionDetailTriggerFunction {
public static void duplicatePDIValidation(Set<Id> promotionIdSet, List<ACC_Promotion_Detail__c> pdList) {
Map<String, ACC_Promotion_Detail__c> productType2PDMap = new Map<String, ACC_Promotion_Detail__c>();
List<ACC_Promotion_Detail__c> promotionPDList = [SELECT ACC_Promotion__c, ACC_Product_Group__c, ACC_Product_Subgroup__c
FROM ACC_Promotion_Detail__c
WHERE ACC_Promotion__c IN :promotionIdSet];
System.debug('promotionPDList: ' + promotionPDList);
if(!promotionPDList.isEmpty()) {
for(ACC_Promotion_Detail__c ppd :promotionPDList) {
productType2PDMap.put(ppd.ACC_Promotion__c+ppd.ACC_Product_Group__c+ppd.ACC_Product_Subgroup__c, ppd);
}
System.debug('productType2PDMap: ' + productType2PDMap);
}
if(productType2PDMap.size() > 0) {
for(ACC_Promotion_Detail__c pd :pdList) {
System.debug('mapValue: ' + productType2PDMap.get(pd.ACC_Promotion__c+pd.ACC_Product_Group__c+pd.ACC_Product_Subgroup__c));
if(productType2PDMap.get(pd.ACC_Promotion__c+pd.ACC_Product_Group__c+pd.ACC_Product_Subgroup__c) != null) {
pd.addError(Label.ACC_Promotion_Detail_Validation_Not_Duplicated);
}
}
}
}
}
总结:优化后的代码,实现了代码分层,避免了for循环里面做查询。