Every MDM-flavoured engagement we take eventually turns into the same conversation. Someone opens the customer table, filters on a company name, and finds eleven rows that are obviously the same organisation spelled eleven ways. The ask that follows is always "can Talend dedupe this," and the honest answer is yes — but the components are the easy part. What decides whether the project works is standardization before matching and survivorship after it.
This tutorial is the pattern we actually deploy: standardize, block, match, review, survive, and keep a crosswalk. It uses tMatchGroup, which ships in the Data Quality palette of Talend Studio, with notes on what to do if you only have the Data Integration components.
Step 1: Standardize before you ever compare
Fuzzy matching on raw data is how you end up with a 0.83 threshold that nobody can defend. Most of the lift comes from cheap normalization applied in a tMap or a Java routine before the match step:
- Uppercase everything, strip accents, collapse repeated whitespace.
- Remove punctuation that carries no meaning: periods, commas, ampersands rendered as
&vsand. - Strip legal-form tokens into a separate column —
LTD,LIMITED,INC,GMBH,PLC,LLC. "Acme Ltd" and "Acme Inc" may or may not be the same entity, and you want that to be a deliberate rule rather than an accident of string distance. - Normalize addresses to a consistent abbreviation set (
STREET→ST,AVENUE→AVE) and split out the postcode. - Null out junk placeholders:
N/A,UNKNOWN,TEST,000000000,noreply@. A thousand records sharing the phone number0000000000will match each other perfectly and ruin your groups.
Do this into new columns, never over the originals. You match on the standardized columns and you output the originals.
If you have the Data Quality palette, tStandardizeRow with a synonym index does the legal-form and street-type work declaratively. If you do not, a tMap expression calling a Java routine is fine, and it is easier to unit-test. Our post on creating and using Java routines covers how to keep those routines shared and versioned.
Step 2: Block, or the job will never finish
Matching is quadratic. Comparing 500,000 records to each other is 125 billion pairs; no amount of memory tuning saves you. Blocking is how you cut that down: you only compare records that share a cheap, coarse key.
Good blocking keys for party data:
- First three characters of the standardized name + postcode outward code.
- Metaphone or Soundex of the name + country.
- Normalized email domain + first letter of surname.
tMatchGroup takes a blocking key directly in its configuration, and it expects the input sorted on that key — put a tSortRow in front of it. Records with different blocking keys are never compared, which is exactly the point and also the risk: a typo in the postcode means a true duplicate is never even considered.
The standard mitigation is multi-pass matching. Run the flow twice with different blocking keys — say name+postcode, then email domain+surname — and union the group assignments. You will catch pairs that any single pass misses. Keep each pass in its own subjob so you can measure what each one contributes; if a pass finds four new pairs on a full run, delete it.
A useful sanity check: log the size of your largest block. If one block holds 40,000 records, your key is too coarse and that block alone will dominate the runtime.
Step 3: Configure tMatchGroup match rules
Inside tMatchGroup you define one or more match rules, each a set of column comparisons with an algorithm, a weight, and a handling of nulls. The component computes a weighted score per pair and assigns a GID (group id) to records above the threshold, plus GRP_SIZE, MASTER, and SCORE columns.
Choosing algorithms:
- Jaro-Winkler — best for personal and company names. It favours matches at the start of the string, which is how names actually vary.
- Levenshtein — good for short codes and typo-heavy free text; expensive on long strings.
- Exact — use it for country, and for any column where a near-match is meaningless.
- Q-grams — reasonable for addresses where word order shifts.
Weights matter more than the algorithm choice. A configuration we reuse for B2B customer data:
| Column | Algorithm | Weight | Null handling |
|---|---|---|---|
| name_std | Jaro-Winkler | 5 | nullMatchNull = false |
| postcode | Exact | 3 | null matches nothing |
| street_std | Q-grams | 2 | null matches nothing |
| country | Exact | 1 | null matches null |
The null handling column is the one people skip. If two records both have a null VAT number and you let null match null, they score a perfect hit on that column and drift over the threshold together. Default to null never matches for every discriminating field.
Set the match interval (threshold) at 0.95 to start and the confidence interval a little lower, around 0.85. Everything between the two lands in the review band rather than being auto-merged.
Step 4: Tune against a labelled sample, not against a feeling
This is the step that separates a dedupe job you can defend from one that quietly destroys data.
Pull a stratified sample of 300–500 candidate pairs from the score output — some near 1.0, some near the threshold, some below it — and have someone from the business label each pair as duplicate or not. Load the labels back in and compute precision and recall at several thresholds with a tAggregateRow:
tFileInputDelimited (labelled pairs)
--> tMap (flag: score >= threshold ? predicted_dup : not)
--> tAggregateRow (count TP, FP, FN by threshold)
--> tLogRow
Run it for thresholds 0.80 through 0.98 in steps of 0.02 and pick the number where precision is where the business needs it. In master data, precision beats recall almost every time: a missed duplicate is an annoyance, a wrongly merged pair of customers is a support incident and sometimes a data-protection one.
Keep the labelled set in source control alongside the job. When someone changes a weight six months from now, this is the regression test — the same idea we apply to jobs generally in testing Talend jobs.
Step 5: Survivorship — building the golden record
tMatchGroup gives you groups. It does not tell you which values win. tRuleSurvivorship will, if you have it, but the rules are the same either way and a tAggregateRow plus tMap will get you there without the Data Quality palette.
Decide survivorship per column, not per record. The rule types worth knowing:
- Most trusted source — CRM wins over the web form, ERP wins over the spreadsheet. Requires a ranked source list, which you should store as a lookup table, not a nested ternary in a
tMap. - Most recent — pick the value from the row with the newest
updated_at. Beware sources that touchupdated_aton every sync. - Most complete — longest non-null value. Good for addresses, bad for names, where the longest is often the one with the department appended.
- Most frequent — the value appearing in the most records in the group. Good for phone and country.
- Longest-lived identifier — the surviving primary key should almost always be the oldest one, because downstream systems already reference it.
Implement it as: group the matched set with tAggregateRow on GID, compute the winning value per column, then join back with a tMap to emit one golden record per group.
Step 6: Never merge in place — write a crosswalk
The output of the job should be two tables, not one:
party_golden— one row perGID, the survived values, and the surviving source key.party_xref— one row per input record: source system, source key,GID, match score, matched-on rule name, and the run timestamp.
The crosswalk is what makes the whole thing reversible. When someone reports that two customers were merged incorrectly, you can find the pair, see which rule matched them and at what score, split the group, and reprocess. Without it you have destroyed the evidence and the only fix is a restore.
It also makes reruns idempotent. Reruns should update the crosswalk and rebuild golden records rather than assigning new group ids, so re-running yesterday's file does not renumber the world. If group ids are exposed downstream, generate them deterministically — a hash of the sorted surviving source keys — instead of using the sequence tMatchGroup hands you.
Step 7: Route the review band to humans
Pairs scoring between the confidence and match intervals go to a review queue, not to the merge. Write them to a table with the two records side by side, the score, and a decision column. Talend Data Stewardship exists for this if you are licensed for it; a simple table plus a spreadsheet export works fine for volumes under a few thousand.
The important part is the feedback loop: every human decision becomes a labelled pair, and every labelled pair goes back into the sample from Step 4. After two or three cycles the review band shrinks noticeably because you have tuned the weights on real disagreements.
Performance notes
tMatchGroupholds a block in memory. Set the max buffer size so that the largest block fits, and enable the temporary-data option to spill to disk when it does not.- Sort on the blocking key upstream in the database if the source is a database.
ORDER BYin the SELECT is dramatically cheaper thantSortRowon a million rows. - The multi-pass union should happen on the database side, not in Talend memory.
- Match only the delta where you can. A daily job that matches new and changed records against the existing golden set — rather than rebuilding all groups nightly — turns a two-hour job into a five-minute one. See the performance tuning post for the memory and parallelism settings that apply here.
What good looks like
On a recent supplier-master project, 640,000 raw records collapsed to 511,000 golden records. Blocking on name-prefix plus country brought the comparison count from ~200 billion pairs to about 40 million. The tuned threshold sat at 0.94, precision measured on a 400-pair labelled sample was 99.2%, and roughly 3,000 pairs a month land in the review queue. The job runs in eleven minutes on the delta.
None of those numbers came from the components. They came from standardizing first, blocking sensibly, and measuring the threshold instead of guessing it.
If you are standing up matching on a party or product master — especially if it is part of a Talend MDM end-of-life migration — get in touch. We have built this pipeline enough times to know where your data will surprise you.