In more cases than not, worrying about efficiency is not the way to approach a problem (i.e. premature optimization is the root of all evil).
The thing to worry about here is how to bulkify your operation. For that, it'll be essentially the same approach in this situation as it is in any situation. Iterate over your trigger records (trigger.new
), and gather Ids of things you want to query into collections, and then perform the query once using
FROM <SObject name> WHERE Id IN :<collection of Ids>
Rinse and repeat for your other related objects.
Store the results in a Map<Id, [SObject name here]>
, and you can iterate over trigger.new
again and grab related records from your various maps as you need them.
There is a second way to do this.
Instead of performing one query per related object, if you make a small sacrifice, you can use a single query.
The sacrifice is to use an After Insert
trigger instead of a Before Insert
trigger. You lose the ability to update your trigger records without DML, but in return your trigger records will have Ids and can be queried.
So, in an After Insert
trigger, you can query your base object A
using WHERE Id IN :trigger.new
, and include, in the SELECT
clause, B.Important_Field__c
, C.Cant_Forget_This_One__c
, D.That_Field__c
, etc...
Of course you will need to perform a DML update on these records, but that's a small price to pay I think.
+edit:
Elaborating on one of my comments.
Trigger.new
is read-only in after triggers. To be able to update the records taking part in an after trigger, you'd need to clone the record in trigger.new
.
e.g.
List<SObject> recordsToUpdate = new List<SObject>();
for(SObject myObj :trigger.new){
SObject cloneObj = myObj.clone();
cloneObj.my_field__c = 'new value';
recordsToUpdate.add(cloneObj);
}
// Instead of update trigger.new ...
update recordsToUpdate;
As for avoiding an infinite loop, you'll need to make sure that you don't update the records in a trigger context variable inside of an update trigger (updating record 'a' causes record 'a' to be updated, which causes record 'a' to be updated, etc...)
You'll also need to make sure you avoid an update trigger from inserting new records that get sent to the update trigger, to create yet more records, etc...
The naive approach to preventing recursion is to set a static boolean variable (declared in an Apex class, not declared in the trigger itself). The step up from that approach is to store a static set of Ids that you've already processed (and check to see if you've already encountered some records, and then remove them, before proceeding to the bulk of your logic).