Salesforce中SOQL如何按Visit.placeID去除查询重复记录
Salesforce SOQL按Visit.placeId去重查询retailvisitkpi实现方案
SOQL本身不支持SQL式的DISTINCT关键字直接对指定字段去重,以下两种方案都可以实现需求,根据业务场景选择即可。
方案1:父对象反向关联查询(性能优先)
如果你的业务场景是每个Visit记录对应唯一的placeId,且每个placeId只需要返回1条匹配的KPI记录,直接从Visit父对象反向查询关联的KPI子记录,天然避免同个Visit下多产品KPI导致的placeId重复问题。
原代码存在两个可优化点:一是重复写了两次相同的Product.Id过滤条件,二是多值匹配用
IN操作符比堆OR条件性能更好、可读性更强。
示例代码:
// 注意替换代码中两个自定义关系的API名为你org内的实际值 String query = 'SELECT Id, placeId, LastModifiedDate, ' + '(SELECT Id, CreatedById, Product.Id, POCGrades__c, fm_pocname__c, Product.Name, fm_poccode__c, CreatedBy.LastName, CreatedDate, ActualBooleanValue ' + 'FROM RetailVisitKPIs__r ' // 替换为Visit对象上关联retailvisitkpi的子关系API名 + 'WHERE CreatedDate = LAST_MONTH ' + 'AND Product.Id IN (\'01t5j000003tszWAAQ\',\'01t5j000003tt5nAAA\',\'01t5j000003tsznAAA\',\'01t5j000003tt1zAAA\',\'01t5j000003tt7AAAQ\') ' + 'LIMIT 1) ' // 每个Visit仅取1条符合条件的KPI,实现placeId维度去重 + 'FROM Visit ' + 'WHERE Id IN (SELECT Visit__c FROM retailvisitkpi ' // 替换为retailvisitkpi上关联Visit的外键字段API名 + 'WHERE CreatedDate = LAST_MONTH ' + 'AND Product.Id IN (\'01t5j000003tszWAAQ\',\'01t5j000003tt5nAAA\',\'01t5j000003tsznAAA\',\'01t5j000003tt1zAAA\',\'01t5j000003tt7AAAQ\'))';
如果存在一个placeId对应多条Visit记录的情况,查询后在Apex层加一次按placeId的去重逻辑即可。
方案2:Apex层Map去重(灵活度优先)
如果需要保留原查询的所有返回字段、自定义去重保留规则(比如保留最新创建的KPI、保留特定评分的KPI),直接查询全量符合条件的记录后,用Apex的Map结构按placeId去重,逻辑可控性最高,也不需要核对复杂的对象关联关系名。
示例代码:
// 注意:跨对象取父对象字段要使用__r关联格式,原代码写的Visit.placeId需要替换为实际外键的__r路径 String query = 'SELECT Id,CreatedById,Product.Id,POCGrades__c,fm_pocname__c,Product.Name,Visit__r.placeId,fm_poccode__c,CreatedBy.LastName,Visit__r.LastModifiedDate,CreatedDate,ActualBooleanValue ' + 'FROM retailvisitkpi ' + 'WHERE CreatedDate = LAST_MONTH ' + 'AND Product.Id IN (\'01t5j000003tszWAAQ\',\'01t5j000003tt5nAAA\',\'01t5j000003tsznAAA\',\'01t5j000003tt1zAAA\',\'01t5j000003tt7AAAQ\')'; List<RetailVisitKPI__c> allMatchedKpis = Database.query(query); Map<String, RetailVisitKPI__c> placeIdDistinctMap = new Map<String, RetailVisitKPI__c>(); for(RetailVisitKPI__c kpi : allMatchedKpis) { String currentPlaceId = kpi.Visit__r.placeId; // 去重规则可自定义:这里示例保留同个placeId下创建时间最新的KPI记录 if(!placeIdDistinctMap.containsKey(currentPlaceId) || kpi.CreatedDate > placeIdDistinctMap.get(currentPlaceId).CreatedDate) { placeIdDistinctMap.put(currentPlaceId, kpi); } } // 最终去重后的结果集 List<RetailVisitKPI__c> distinctResult = placeIdDistinctMap.values();
这个方案只要控制单次查询的记录数不超过Salesforce Governor Limit(单次查询5w条上限),零售场景按月度查询数据量完全可以支撑。
常见坑点提醒
- 不要尝试在SOQL中写
DISTINCT关键字,SOQL无此语法,会直接抛编译错误 - 跨对象查询父记录字段时,必须通过外键字段的
__r关系取属性,直接写Visit.placeId会报字段不存在错误,如果是托管包下发的字段,需要加上对应包的命名空间前缀 LAST_MONTH是SOQL原生支持的日期字面量,不需要额外加引号,原代码这部分写法正确。
内容的提问来源于stack exchange,提问作者김지호
相关产品推荐
相关产品推荐

