The reserved dedupe rules don't tell us what they are doing behind the seens. The name and email (reserved) rule says:

This reserved rule is pre-configured with matching fields to optimize dedupe scanning performance. It matches on:

  1. Email
  2. First Name
  3. Last Name

But it doesn't reveal the length, weight or threshold?

I'm kind of assuming that all three have to match exactly for civi to not create a duplicate record (unservised) or for it to not pop-up a duplicate warning (supervised).

Am I correct?


2 Answers 2


From the DB I can see that dedupe_rule_group_id 1 (in civicrm_dedupe_rule) has following rows

id dedupe_rule_group_id rule_table rule_field rule_length rule_weight 1 1 civicrm_contact first_name NULL 5 2 1 civicrm_contact last_name NULL 7 3 1 civicrm_email email NULL 10

and from civicrm_dedupe_rule_group we have 'threshold = 20'

id contact_type threshold used name title is_reserved 1 Individual 20 Supervised IndividualSupervised Name and Email (reserved) 1

ie First 5 Last 7 Email 10 Threshold 20

BUT - Jon points out that this is just a useless remnant from our evolutionary past so take his answer as the valid one.

  • 1
    This is the common-sense answer, but there's a special case at work here. Be glad you haven't had cause to find out the truth the hard way! Dec 20, 2016 at 6:26

Pete's answer should be correct, but reserved dedupe rules don't work like normal rules. The original assumption: First name, last name and email must all be identical - is correct.

Dedupe rules with multiple criteria work by running a separate query for each criterion, filling a temp table with the weights. This is flexible but inefficient, so the "reserved" queries are done using a single handwritten SQL query. This particular query is found in CRM_Dedupe_BAO_QueryBuilder_IndividualSupervised::internal and overrides the stored query in the database (which is the old query from before reserved queries existed). Here's the query that runs.

        SELECT contact1.id as id1, contact2.id as id2, {$rg->threshold} as weight
        FROM civicrm_contact as contact1
          JOIN civicrm_email as email1 ON email1.contact_id=contact1.id
          JOIN civicrm_contact as contact2 ON
            contact1.first_name = contact2.first_name AND
            contact1.last_name = contact2.last_name
          JOIN civicrm_email as email2 ON
            email2.contact_id=contact2.id AND
        WHERE contact1.contact_type = 'Individual'");
  • Thanks Jon - I edited mine, but could delete if it makes more sense.
    – petednz - fuzion
    Dec 20, 2016 at 18:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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