Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I understand from experience that Master-Detail deletion triggers do not cascade. For example if I delete my Account the Contact deletion triggers don't fire.

Having devoured the first and second editions of the outstanding book Advanced Apex by Dan Appleman, there's one point in the chapter on triggers I'm looking to clarify:

Delete triggers typically do not fire on cascade deletes. If you need to detect deletion of the child objects, you must create a before-delete trigger on the parent object and perform your desired operation on the child objects at that time (be sure to design this carefully - there may be a large number of child objects).

So if I had a trigger like this on Contact:

trigger ContactBeforeDelete on Contact (before delete) {

The book encourages creating one likewise on Account:

trigger AccountBeforeDelete on Account (before delete) {
    List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :id];

But to me, the obvious solution would be to cascade the deletion, not the business logic:

trigger AccountBeforeDelete on Account (before delete) {
    List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :id];
    delete contacts;

The child triggers will take care of the logic. Copying the invocation around isn't DRY. So why 'perform the desired operation on the child objects' instead of just cascading the delete? Is there some catch?

share|improve this question
up vote 10 down vote accepted

One of the issues with an explicitly delete of all the child records is that there may simply be too many of them. I've seen instances where a master object has more than 10,000 detail records, and if you're doing a mass delete of parents with high numbers of children that would eat through your DML rows limit very quickly.

While it's conceptually simpler to cascade the deletion I'd think carefully about expected volumes before doing so. Dan's pattern only cascades side effects of the deletion as needed.

It boils down to developer productivity vs efficient code, and every environment has different factors impacting which way that decision should go.

Edit: You'd also have to be wary of any developers taking advantage of the fact that cascade deletion doesn't invoke delete triggers and assuming that all delete operations on child records are explicit in their triggers. In a complex org that's a headache to check for.

share|improve this answer
Either method will fail with too many rows anyways; odds are, you'll run out of query row results before you run out of DML statements, since both are the same value (50,000), but the query generally has to come before the DML operation. Good point about developers intentionally hoping that the triggers are not called, although I would argue against that design on the principle that if they fix cascade deletions, they'd break those triggers (it's more of a "bug exploit"). Note also that there is a hard limit on cascade deletes anyways that's only twice the DML/query limit. – sfdcfox Dec 30 '13 at 20:14
Also, I don't think anyone here is advocating manually invoking deletions for no reason, so either pattern is equally viable from a technological stand-point. However, the DRY method has one advantage: if they fix cascade deletes, it will still work, while the other version might not (because the trigger's functionality will be called twice because of code duplication). – sfdcfox Dec 30 '13 at 20:19
@sfdcfox the total number of DML rows is currently 10k, 20% of the query row limits, large, but not as large as the query limits. I do know about the hard limit (although not the threshold), some company cough kept crashing pods by overflowing the max transaction memory in oracle. – ca_peterson Dec 30 '13 at 21:48
Oh... good catch. I forgot about that bit. Still, it's hard to imagine you'd want to intentionally skip a trigger, since its usually there to protect some other data. – sfdcfox Dec 30 '13 at 22:02
... I'm going to just stop now. See, I just realized that sometimes skipping the trigger would be good. In fact, I think I have a use for that right now... – sfdcfox Dec 30 '13 at 22:09

The statement is ambiguous when read and re-read carefully. "Desired operation" may refer to calling the business logic, or it may refer to simply calling a DML statement (as your DRY example). I personally follow the second method, because it not only simplifies your code, but also reduces the number of paths one has to scan through when a logic bug or error appears. It appears that this is more an editorial error rather than being intentionally misleading, but I would personally reach out to the author for clarification.

share|improve this answer
gotcha - by 'desired operation' Dan Appleman may mean the deletion after all – bigassforce Dec 30 '13 at 16:03
@user320 And ca_peterson made a valid point. Both approaches mean something, and both have a practical use. Sometimes you do actually need to duplicate code, if the deletion isn't significant to the processing at hand. For example, a rollup summary trigger probably doesn't need to fire if the parent record is also going to vanish, etc. So, both are correct, but conditionally so. Both are tools to be used with care. – sfdcfox Dec 30 '13 at 22:12
thanks for your detailed and thoughtful commentary – bigassforce Dec 31 '13 at 0:06

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.