Hello, I am working on a project that involves collecting data from various sources. I have a scenarios where I have a final table of more than 800k records.
Since there are multiple sources there are certain sources which are preferred for a column and due to this I have a table which contains weightage given to a column from a certain source.( Please refer to the table below that will explain it better). Basically it is the mapping of weights (the lower the number, higher will be the priority) through which the column value is given precedence over the same column value from different source.
Sample weightage table:
| columns |. source1. |. source2. |. source3 |...| source10|
|-------------|-----------|-----------|-----------|---|---------|
|first_name | 1 |2 |3 |...|10 |
|last_name. |3 |1 |2 |...|9 |
|mobile |2 |3 |5 |...|8 |
|email |2 |3 |5 |...|8 |
|city |3 |5 |1 |...|9 |
Now I have a created a look up table for the identification of source system based on numbers. Basically I have assigned a number to a source system. (PS: I need this table for other action items as well. Can't get away with this.) Here is a sample table:
|source system|ID|
|-------------|--|
|source1 |11|
|source3. |12|
|source4. |13|
|source5. |14|
|source6. |15|
Now, let's come to the final table. This is created post cleansing and has all the records. The table contains around 90 columns.
|first_name |. last_name. |. mobile. |. email |city. |source system ID|
|-------------|-------------|----------|-------------|---------|----------------|
|john | doe |12345667 |john@xyz.com |london |12 |
|j | |12345667. |jack@abc.com |new york |13 |
|john |d |12345667. |john@abc.com | |11 |
|jack |dawn |344567754 |jack@abc.com |amsterdam|11 |
|j |dawn |344567754 |jack@abc.com |zurich |13 |
Now I need to merge the records (let's say on mobile or email) from different source systems. Basically each row is from a different source system. For this what I have to do this I'll gather the records based on mobile (and the later on email) then I'll check each column from weight table. Where ever I find the record with less weight it means it has more priority and I'll consider the value of that source system.
Finally after matching all these records I have to generate 1 single record. Now the problem is I have to do this on:
- on source table alone and
- also on the input data table (that is for upcoming data; so have to match the upcoming record from the existing records)
Here is how I am doing it for source table only scenario. Below is the excerpt of the UDF that I have created:
def match_merge_surviver(col, col_input, inp_source_system_id, mobile):
inp_weight = '999'
temp = []
first_name = {
'17':1,
'16':9,
'14':8,
'4':4,
'13':3,
'18':5,
'19':2,
'20':1,
'21':1,
'23':6,
'11':999,
'24':1
}
if(not col_input or col_input =='Not Provided' or col_input == 'Null'):
return col_input
if col =='first_name':
temp={}
try:
if temp:
if temp[mobile] > inp_weight:
temp[mobile] = inp_weight
return col_input
else:
pass
else:
inp_weight = first_name[str(inp_source_system_id)]
temp[mobile] = inp_weight
return col_input
except KeyError:
# Key is not present
inp_weight = 999
pass
#Registering function
sqlContext.udf.register("survivorship", survivorship)
Here is this Spark SQL query I am using for the application of UDF. Please note in this approach I have to run this UDF on each of 80+ columns. I am showing this only for first_name. Also, in where clause I have given mobile but it needs to run for thousands pf records.
```
%sql
select a.email, a.source_system_id, a.mobile, a.DOB, a.first_name,
match_merge_surviver('first_name',a.first_name, cast(a.source_system_id as
string),a.mobile)
as sur_first_name
from final_table a
where a.source_system_id <> 0
and a.source_system_id is not null
and a.mobile = '12345667'
```
The output after above operation is:
|first_name |. last_name. |. mobile. |. email |city. |source system ID|
|-------------|-------------|----------|------------|--------|----------------|
|john | doe |12345667 |john@xyz.com|london |12 |
|j | |12345667. |jack@abc.com| |13 |
|john |d |12345667. |john@abc.com|brooklyn|11 |
Now it's not very effective since now I have to do a complex and expensive join using something like :
```
ROW_NUMBER() OVER (PARTITION BY mobile ORDER BY survived_first_name,
modified_date desc )
```
The output should be like a 1 single record with all matching done:
|first_name |. last_name. |. mobile. |. email |city |source system ID|
|-------------|-------------|----------|-------------|-------|----------------|
|john | doe |12345667 |john@xyz.com |london |12 |
which again is not very effective and since I am already creating a UDF taking a performance hit.
Can you please help me improve this approach and if possible suggest a non UDF way. I'll really appreciate your help. Apologies for the long post but I needed to and tried to explain the whole process. Any further questions are highly appreciated.
