demo1:db<>fiol , demo2:db<>fiol
WITH combined AS (
SELECT
a.email as a_email,
b.email as b_email,
array_remove(ARRAY[a.id, b.id], NULL) as ids
FROM
a
FULL OUTER JOIN b ON (a.email = b.email)
), clustered AS (
SELECT DISTINCT
ids
FROM (
SELECT DISTINCT ON (unnest_ids)
*,
unnest(ids) as unnest_ids
FROM combined
ORDER BY unnest_ids, array_length(ids, 1) DESC
) s
)
SELECT DISTINCT
new_id,
unnest(array_cat) as email
FROM (
SELECT
array_cat(
array_agg(a_email) FILTER (WHERE a_email IS NOT NULL),
array_agg(b_email) FILTER (WHERE b_email IS NOT NULL)
),
row_number() OVER () as new_id
FROM combined co
JOIN clustered cl
ON co.ids <@ cl.ids
GROUP BY cl.ids
) s
Steg för steg förklaring:
Som förklaring tar jag denna datauppsättning. Det här är lite mer komplext än ditt. Det kan illustrera mina steg bättre. Vissa problem uppstår inte i din mindre uppsättning. Tänk på tecknen som variabler för e-postadresser.
Tabell A:
| id | email |
|----|-------|
| 1 | a |
| 1 | b |
| 2 | c |
| 5 | e |
Tabell B
| id | email |
|----|-------|
| 3 | a |
| 3 | d |
| 4 | e |
| 4 | f |
| 3 | b |
CTE combined
:
GÅ MED i båda tabellerna på samma e-postadresser för att få en kontaktpunkt. ID:n för samma Id:n kommer att sammanfogas i en array:
| a_email | b_email | ids |
|-----------|-----------|-----|
| (null) | [email protected] | 3 |
| [email protected] | [email protected] | 1,3 |
| [email protected] | (null) | 1 |
| [email protected] | (null) | 2 |
| (null) | [email protected] | 4 |
CTE clustered
(ursäkta namnen...):
Målet är att få alla element exakt i endast en array. I combined
Du kan till exempel se att det för närvarande finns fler arrayer med elementet 4
:{5,4}
och {4}
.
Ordna först raderna efter längden på deras ids
matriser eftersom DISTINCT
senare bör ta den längsta arrayen (eftersom håll beröringspunkten {5,4}
istället för {4}
).
Sedan unnest
ids
arrayer för att få en grund för filtrering. Detta slutar på:
| a_email | b_email | ids | unnest_ids |
|---------|---------|-----|------------|
| b | b | 1,3 | 1 |
| a | a | 1,3 | 1 |
| c | (null) | 2 | 2 |
| b | b | 1,3 | 3 |
| a | a | 1,3 | 3 |
| (null) | d | 3 | 3 |
| e | e | 5,4 | 4 |
| (null) | f | 4 | 4 |
| e | e | 5,4 | 5 |
Efter filtrering med DISTINCT ON
| a_email | b_email | ids | unnest_ids |
|---------|---------|-----|------------|
| b | b | 1,3 | 1 |
| c | (null) | 2 | 2 |
| b | b | 1,3 | 3 |
| e | e | 5,4 | 4 |
| e | e | 5,4 | 5 |
Vi är bara intresserade av ids
kolumn med de genererade unika id-klustren. Så vi behöver alla bara en gång. Detta är jobbet för den senaste DISTINCT
. Så CTE clustered
resulterar i
| ids |
|-----|
| 2 |
| 1,3 |
| 5,4 |
Nu vet vi vilka id som är kombinerade och bör dela deras data. Nu går vi med i klustrade ids
mot ursprungstabellerna. Eftersom vi har gjort detta i CTE combined
vi kan återanvända den här delen (det är anledningen till att den är outsourcad till en enda CTE förresten:vi behöver inte längre ansluta till båda tabellerna i det här steget). JOIN-operatören <@
säger:JOIN om "touch point"-arrayen av combined
är en undergrupp av id-klustret för clustered
. Detta ger:
| a_email | b_email | ids | ids |
|---------|---------|-----|-----|
| c | (null) | 2 | 2 |
| a | a | 1,3 | 1,3 |
| b | b | 1,3 | 1,3 |
| (null) | d | 3 | 1,3 |
| e | e | 5,4 | 5,4 |
| (null) | f | 4 | 5,4 |
Nu kan vi gruppera e-postadresserna genom att använda de klustrade ID:n (kolumnen längst till höger).
array_agg
samlar e-postmeddelanden från en kolumn, array_cat
sammanfogar e-postmatriserna för båda kolumnerna till en stor e-postmatris.
Eftersom det finns kolumner där e-post är NULL
vi kan filtrera bort dessa värden innan vi klusterar med FILTER (WHERE...)
klausul.
Resultat hittills:
| array_cat |
|-----------|
| c |
| a,b,a,b,d |
| e,e,f |
Nu grupperar vi alla e-postadresser för ett enda id. Vi måste skapa nya unika ID. Det är vad fönsterfunktionen
row_number
är för. Det lägger helt enkelt till ett radantal i tabellen:
| array_cat | new_id |
|-----------|--------|
| c | 1 |
| a,b,a,b,d | 2 |
| e,e,f | 3 |
Sista steget är att unnest
arrayen för att få en rad per e-postadress. Eftersom det fortfarande finns några dubbletter i arrayen kan vi eliminera dem i detta steg med en DISTINCT
likaså:
| new_id | email |
|--------|-------|
| 1 | c |
| 2 | a |
| 2 | b |
| 2 | d |
| 3 | e |
| 3 | f |