我正在开发一个模块,它会在插入/更新时重复删除联系人记录,但是我遇到了超出Apex CPU时间限制的错误。我理解为了克服它,我们需要稍微优化代码,但在下面的代码块中,似乎很少有优化它的范围。任何帮助将不胜感激。
在帐户上我们有一个多重名单,我们可以从中选择字段,根据这些字段定义此帐户下联系人的唯一性。差异账户可能有所不同。以下代码是触发器的处理程序类的一部分,其中我们有一个旧的联系人列表的映射,其中帐户ID为关键字(mapOfAccountIdWithItsContact),并且具有新联系人列表和帐户作为键的地图(newContactWithAccountMap)我们迭代这些映射我们在triiger中获得联系人的帐户集。我们有一个地图可以用来确定每个帐户的联系唯一性字段(mapOfAccountWithFilters)。
以下是代码段:
for(String accountId : accountIdSet){
if(newContactWithAccountMap.get(accountId) != null){
for(Contact newContact : newContactWithAccountMap.get(accountId)){
for(Contact oldContact : mapOfAccountIdWithItsContact.get(accountId)){
//Check for duplication only in the respective account, also this should not apply on insertion of Office contact
matchingContactFound = false;
if(oldContact.id != newContact.id){ //while insert, newContact's id will be null and while update it will verify that it is not matching itself with its old record.
for(String filterFieldName : mapOfAccountWithFilters.get(accountId)){
if(oldContact.get(filterFieldName) == newContact.get(filterFieldName)){
matchingContactFound = true;
//If match is found update last de duplication date to today on old contact
oldContact.Last_De_Duplication_Date__c = System.Today();
oldContactsToUpdateSet.add(oldContact);
}else{
matchingContactFound = false;
break; //get another "old contact"
}
}
}
if(matchingContactFound){
//stop it from being inserted
duplicateContactSet.add(newContact.Id);
//newContact.addError('Contact cannot be inserted because a contact is already present based on the Master Target Identifier at client level.');
break; //get another "new contact"
}
}
}
}
}
任何帮助避免4循环或替代方法将非常感激。提前谢谢。
答案 0 :(得分:4)
好问题!
但如果没有看到更多代码,很难说些什么......
Map<Id, List<Contact>
等,而不是试图从描述中弄清楚)要考虑的另一件事是多重列表中有多少个字段?您是否考虑过提前做好准备?例如,在每个插入物和每次更新都会保存&#34;重要字段中的值&#34;在联系人的帮助Text(255)
字段中,我们将其称为&#34;重复类别/细分/标记&#34;或类似的东西。将该字段标记为外部id(它不必是唯一的,ext.id足以将其编入索引)。
然后你应该可以非常快速地选择帐户ID和那个&#34;类别&#34;火柴。如果字段值相同或长度为255,则表示它被截断,您必须运行精确的逐字段比较。但是,如果没有相同的&#34;类别&#34; - 你可以安全地说没有重复。
这样的事情必须经过精心设计(触发联系人,但也可以触发#34;重新计算&#34;每次字段定义更改时此字段...以及是否可以指出联系其他记录(比如用户),那么您也需要保护自己免受用户更改......)但我说绝对值得一试。你甚至不必支持所有领域。先说一下&amp;姓氏,电子邮件,电话,地址之一。这应该已经成为了一个巨大的帮助&#34; bucketing&#34;他们然后只从正确的桶中查询。
缓存您的来电
您有很多地图get
来电。你在循环中调用System.today()
(我会理解System.now ......但今天?)。检查this interesting short video并尽可能地拥有更多局部变量和更少的脚本语句。对于给定的帐户,字段列表没有变化 - 所以为什么每次迭代都会从地图中痛苦地获取它。
检查逻辑
if(newContactWithAccountMap.get(accountId) != null)
- 这绝不会发生。如果没有联系人,那么&#34;&#34; acc被插入 - 无论如何你在做什么。你在这里过度保护还是实际需要这种检查(这会暗示一些更大的问题)。即使对于私人联系人(使用AccountId = null),您也不应该获取联系人并为不受影响的帐户构建地图。
所以在第一次迭代中,我做了类似的事情(这是一个小改进,但谁知道,可能有所帮助):
for(String accountId : accountIdSet){
List<String> fields = mapOfAccountWithFilters.get(accountId); // it's always same for that Account, isn't it?
for(Contact newContact : newContactWithAccountMap.get(accountId)){
for(Contact oldContact : mapOfAccountIdWithItsContact.get(accountId)){
if(oldContact.id != newContact.id){
Boolean allFieldsMatch = true;
for(String fieldName : fields){
if(allFieldsMatch &= (oldContact.get(fieldName) == newContact.get(fieldName))){
oldContactsToUpdateSet.add(oldContact);
}else{
break;
}
}
if(allFieldsMatch) {
duplicateContactSet.add(newContact.Id);
break;
}
}
}
}
}
Date today = System.today();
for(Contact c : oldContactsToUpdateSet){
c.Last_De_Duplication_Date__c = today;
}
第二遍
如果它没有帮助 - 你可能会尝试在这里加入我的想法。让我们说你已经将3个联系人插入到已经拥有10的帐户中了。内循环中的3 * 10 = 30个比较(他们都是全新的,所以比较的诀窍旧的和新的联系人的ID没有帮助。
但是,如果你准备某种类似这样的复合键&#34; bucket&#34;我已经谈过你了,真的把它弄平了。
for(String accountId : accountIdSet){
List<String> fields = mapOfAccountWithFilters.get(accountId);
Set<String> oldContactBuckets = new Set<String>();
/* I'm cheating here a bit.
All I want to know is whether there was a match. I don't care with which Contact.
If you care - you'd have to convert this Set<String> to Map<String, Set<Id>> for example.
Looks like you do care because you're setting this Last_De_Duplication_Date__c
but I'll leave it as exercise for the reader :P
*/
for(Contact oldContact : mapOfAccountIdWithItsContact.get(accountId)){
oldContactBuckets.add(buildKey(oldContact, fields));
}
for(Contact newContact : newContactWithAccountMap.get(accountId)){
String key = buildKey(newContact, fields);
if(oldContactBuckets.contains(key)){
duplicateContactSet.add(newContact.Id);
}
}
}
private String buildKey(Contact c, List<String> fields){
List<String> temp = new List<String>();
for(String fieldName : fields){
temp.add(String.valueOf(c.get(fieldName)));
}
return String.join(temp, '\n'); // pick a field separator that's unlikely to appear in your real data. Tab maybe?
}
这将首先构建10个密钥,然后与传入数据的3个密钥进行比较。意思是只有13个等同于你的&#34;最里面的&#34;循环。
如果这对您的情况没有帮助 - 您可以考虑将其重写为批量顶点我猜...
答案 1 :(得分:0)
部分答案可能是将此作为批处理过程。运行批处理代码时,处理的每个单独批处理记录都有自己的时间限制。批量顶点here还有更多内容。
您不会显示SOQL查询或了解帐户可能拥有的联系人数量。帐户和联系人的总数也很有用。没有这些,就很难确定问题所在。您可能需要查看SOQL优化。例如,像“not equals”(“!=”)这样的运算符效率低下。关于这个主题还有更多可以说的内容,你可以阅读它here。
另一种可能性是您可以使用一个查询替换多个查询。在类似的情况下,我可以编写代码,每个站点执行一次查询(自定义对象)。这将限制我100个站点。相反,我对所有网站进行了查询(我们远远少于50,000限制),然后创建了一个地图,通过唯一的密钥存储查询结果。