Is there a quick/scalable/reliable way to get a list of the fields that changed before an update is made to a record? I know i get the bulk (maybe all even) in a before update trigger on the record, but I'm not sure if that would get all them?
-
3You can determine which fields changed, but if you have many fields and you perform large updates (such as with the data loader), you will run into maximum CPU time limits. So scalable is a relative term. The only truly safe way to handle this would be to check what you can in real time and if you runout of time, divert to a batch process. On the other hand, you should be fine if you limit yourself to a whitelist of fields to monitor, such as maybe 100 or so fields. I'm not sure what your use case is, so I don't necessarily have an answer for you.– sfdcfoxMay 6, 2014 at 0:37
-
1Great Question, I would add, I know that only changed fields get processed in an update trigger, but being able to tell exactly which fields changed, in some straightforward manner (and in mass), would be very helpful.– JimRaeMay 6, 2014 at 12:09
4 Answers
I think that what you need is something like the method below, you can create a list of Schema.sObjectField to include/exclude certain fields.
In your trigger just call this method with the record Id.
public Schema.sObjectField[] getChangedFields(ID recordId, Schema.sObjectField[] fieldList) {
Schema.sObjectField[] changedFields = new list<Schema.sObjectField> ();
SObject o1 = Trigger.oldMap.get(recordId);
SObject o2 = Trigger.newMap.get(recordId);
for (Schema.sObjectField field : fieldList) {
Object v1 = o1.get(field);
Object v2 = o2.get(field);
if (didFieldChange(v1, v2)) {
changedFields.add(field);
}
}
return changedFields;
}
private static Boolean didFieldChange(Object v1, Object v2) {
if (v1 == null && v2 == null) {
return false;
}
if (v1 != v2) {
return true;
}
return false;
}
Please mark this answer as accepted if this works for you and for the other readers bump up if it helped you.
/**
* @description checks whether the updated record values are different from the previous
* @author Remario Richards | 05-29-2021
* @param oldRecord
* @param newRecord
* @return Set<String>
**/
public static Set<String> GetChangedFields(SObject oldRecord, SObject newRecord) {
Set<String> fields = new Set<String>();
Set<String> systemFields = new Set<String>{
'CreatedDate','LastModifiedById','LastModifiedDate','SystemModstamp'
};
for (String key : newRecord.getPopulatedFieldsAsMap().keySet()) {
if(systemFields.contains(key)) continue;
if(oldRecord.get(key) != newRecord.get(key)){
fields.add(key);
}
}
return fields;
}
In a before update trigger, you can use trigger.old and trigger.new to compare the old and new values/fields. Then you can call another method to store the fields that are changed or process them in anyway you need.
Considering that you want the solution to be scalable before updating, you can schedule an Apex job or Batch Apex for the records that need to be processed (future will not work).
We can get the changed fields by enabling field history tracking. But we can enable history only for 20 fields per object. Then by access history object we can get both old value and new value.