demo1:db<>fiddle , demo2:db<>fiddle
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
Vysvětlení krok za krokem:
Pro vysvětlení vezmu tento datový soubor. Tohle je trochu složitější než to tvoje. Může lépe ilustrovat mé kroky. Některé problémy se ve vaší menší sadě nevyskytují. Přemýšlejte o znacích jako o proměnných pro e-mailové adresy.
Tabulka A:
| id | email |
|----|-------|
| 1 | a |
| 1 | b |
| 2 | c |
| 5 | e |
Tabulka B
| id | email |
|----|-------|
| 3 | a |
| 3 | d |
| 4 | e |
| 4 | f |
| 3 | b |
CTE combined
:
PŘIPOJTE se k oběma stolům na stejné e-mailové adrese, abyste získali kontaktní bod. ID se stejnými ID budou zřetězena do jednoho pole:
| 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
(omlouvám se za jména...):
Cílem je dostat všechny prvky přesně do jednoho pole. V combined
můžete vidět, například aktuálně existuje více polí s prvkem 4
:{5,4}
a {4}
.
Nejprve seřaďte řádky podle délky jejich ids
pole, protože DISTINCT
později by mělo trvat nejdelší pole (protože držení dotykového bodu {5,4}
místo {4}
).
Poté unnest
ids
polí získat základ pro filtrování. Toto končí:
| 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 |
Po filtrování pomocí 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 |
Zajímají nás pouze ids
sloupec s vygenerovanými jedinečnými id clustery. Všechny je tedy potřebujeme jen jednou. Toto je úloha posledního DISTINCT
. Takže CTE clustered
výsledky v
| ids |
|-----|
| 2 |
| 1,3 |
| 5,4 |
Nyní víme, která ID jsou kombinována a měli bychom sdílet svá data. Nyní připojíme seskupená ids
proti tabulkám původu. Protože jsme to udělali v CTE combined
můžeme tuto část znovu použít (to je mimochodem důvod, proč je outsourcována do jediného CTE:V tomto kroku již nepotřebujeme další spojení obou tabulek). Operátor JOIN <@
říká:JOIN, pokud je pole "touch point" combined
je podskupinou id clusteru clustered
. Výsledkem je:
| 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 |
Nyní jsme schopni seskupit e-mailové adresy pomocí seskupených ID (sloupec zcela vpravo).
array_agg
agreguje e-maily jednoho sloupce, array_cat
zřetězí pole e-mailů obou sloupců do jednoho velkého pole e-mailů.
Protože existují sloupce, kde je email NULL
tyto hodnoty můžeme před shlukováním odfiltrovat pomocí FILTER (WHERE...)
klauzule.
Dosavadní výsledek:
| array_cat |
|-----------|
| c |
| a,b,a,b,d |
| e,e,f |
Nyní seskupujeme všechny e-mailové adresy pro jedno jediné ID. Musíme vygenerovat nová unikátní ID. To je to, co funkce okna
row_number
je pro. Jednoduše přidá počet řádků do tabulky:
| array_cat | new_id |
|-----------|--------|
| c | 1 |
| a,b,a,b,d | 2 |
| e,e,f | 3 |
Posledním krokem je unnest
pole získat řádek na e-mailovou adresu. Protože v poli jsou stále nějaké duplikáty, můžeme je v tomto kroku odstranit pomocí DISTINCT
také:
| new_id | email |
|--------|-------|
| 1 | c |
| 2 | a |
| 2 | b |
| 2 | d |
| 3 | e |
| 3 | f |