pixiAddressesFindDuplicates
Finds probable duplicate customers by fuzzy address matching in batches of 2500 addresses and stores scored customer pairs in Addresses_Duplicates.
Batch job that detects duplicate customers by comparing their addresses. It returns no result set. It writes its findings to the table Addresses_Duplicates.
Time window
The procedure only works between 21:00 and 06:59 server time. If the current hour is between 7 and 20, it prints a message and ends without doing anything. The only exception is a session whose CONTEXT_INFO is 0x00999, which is used for unit tests.
Steps
-
Creates the helper tables
tmp.Addresses_Processandtmp.Addresses_Simpleif they do not exist yet. -
If
ReCreateAll= 1, emptiesAddresses_Duplicates,tmp.Addresses_Simpleandtmp.Addresses_ProcesswithTRUNCATE. All existing matches are deleted. -
Selects up to 2500 customer main addresses (
Customers.AddrRef) as candidates, newest address key first. A candidate must meet all of these conditions:- It is not yet in
tmp.Addresses_Simple. - Its key is above the highest or below the lowest key already processed.
- The customer has no order with an order header attribute of type 3 (archived orders).
- It is not yet in
-
Removes titles from the names:
Frau,Herr,Prof.,Dipl.,Ing.,Mag.,med.,Dr.. If the last name is empty, the fieldNameis split at the first space into first name and last name. -
Stores a simplified copy of each address in
tmp.Addresses_Simple:- the first 3 characters of
SOUNDEXfor street, city, last name, first name and CareOf (company); - the cross sum of the ZIP code (
fCrossSum); - address, customer and shop ID.
- the first 3 characters of
-
Marks up to 2500 of these addresses as processed in
tmp.Addresses_Process, newest first. -
Compares the newly processed addresses with all simplified addresses. A pair counts as a match if all of these conditions hold:
- different customer;
- same shop;
- same soundex code for city, street and CareOf;
- same ZIP cross sum;
- same soundex codes for first and last name, also when first and last name are swapped.
A customer is skipped if it already appears as
CustRef1orCustRef2inAddresses_Duplicates. -
Gives each pair a score (
MatchLevel). The score is the sum of these parts:- name: 40 full, 30 swapped, 20 soundex match, 10 weak match or different first name;
- street: 20 full, 10 partial, 5 weak;
- city: 20 full, 10 partial, 5 weak;
- ZIP: 20 full, 10 same cross sum;
- company (CareOf): 0 full, -10 partial, -15 weak.
MatchTextlists the non-full match flags, for exampleSTREET_PARTIAL_MATCH;, cut to 100 characters. -
Inserts each new pair once.
CustRef1/AddrRef1is the newly processed customer andCustRef2/AddrRef2is the matched customer. Pairs that already exist in either direction are skipped. -
If
tmp.Addresses_Simplestill contains addresses that are not intmp.Addresses_Process, the procedure queuesEXEC pipiAddressesFindDuplicatesinExecQueries. In this way the run continues in the next batch.
Notes
- No result set: the call returns no rows. Informational output comes only via
PRINT, for exampleRUN BEFORE 07:00 or AFTER 20:00during the day. These messages may appear asSqlMessageentries, depending on the client. A daytime call therefore looks successful but does nothing. - ReCreateAll in the daytime: the truncation for
ReCreateAll= 1 happens only after the time check. During the day the parameter has no effect. - Continuation: the follow-up run is queued in
ExecQueries. How and when that queue is executed is not part of this code. A comment speaks of "1 minute" and of runs until 7 AM. The batch size is 2500 addresses, although a comment mentions 100. - Permanent helper tables: the procedure creates and fills the permanent tables
tmp.Addresses_Simpleandtmp.Addresses_Processto remember its progress between runs. - No transaction: all reads use
NOLOCKand there is no explicit transaction. - Pitfalls visible in the code:
- The partial checks for street and city compare an address with itself. Whenever street or city do not match exactly, the result is therefore always
STREET_PARTIAL_MATCH;orCITY_PARTIAL_MATCH;. The_LOWvariants never occur. - Pairs are numbered per sum of the two customer IDs (
s.CustRef + c.CustRef). Different pairs whose IDs add up to the same value can therefore be dropped, because only the first one is kept. - If the names match only in swapped soundex form but not exactly swapped, the name category is empty.
MatchLevelandMatchTextthen become NULL.
- The partial checks for street and city compare an address with itself. Whenever street or city do not match exactly, the result is therefore always
- Runtime: matching is CPU-intensive. A code comment warns not to increase the batch size.
Tables used
| Parameter | Type | Default | Description |
|---|---|---|---|
ReCreateAll
|
bit boolean |
0 |
If 1, deletes all existing duplicate matches and the progress tables and starts the matching from scratch. This is effective only inside the nightly time window. Allowed values
Example: |
This call returns no result set.
POST https://apigateway.descartes.com/tms/pixi/
Content-Type: text/xml; charset=utf-8
Authorization: Basic BASE64(USERNAME:PASSWORD)
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="https://apigateway.descartes.com/tms/pixi/">
<SOAP-ENV:Body>
<ns1:pixiAddressesFindDuplicates>
<!-- <ReCreateAll>0</ReCreateAll> -->
</ns1:pixiAddressesFindDuplicates>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
curl -X POST 'https://apigateway.descartes.com/tms/pixi/' \
--user 'USERNAME:PASSWORD' \
--header 'Content-Type: text/xml; charset=utf-8' \
--data-binary @- <<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="https://apigateway.descartes.com/tms/pixi/">
<SOAP-ENV:Body>
<ns1:pixiAddressesFindDuplicates>
<!-- <ReCreateAll>0</ReCreateAll> -->
</ns1:pixiAddressesFindDuplicates>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
XML
<?php
$soapClient = new SoapClient(null, [
'login' => 'USERNAME',
'password' => 'PASSWORD',
'location' => 'https://apigateway.descartes.com/tms/pixi/',
'uri' => 'https://apigateway.descartes.com/tms/pixi/',
]);
$arguments = [
// 'ReCreateAll' => '0',
];
$soapArguments = [];
foreach ($arguments as $name => $value) {
$soapArguments[] = new SoapVar($value, XSD_STRING, '', '', $name);
}
$response = $soapClient->__soapCall('pixiAddressesFindDuplicates', $soapArguments);
- SQL object:
- dbo.pipiAddressesFindDuplicates