demo1:db<>violino , demo2:db<>violino
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
Spiegazione passo passo:
Per la spiegazione prenderò questo set di dati. Questo è un po' più complesso del tuo. Può illustrare meglio i miei passi. Alcuni problemi non si verificano nel tuo set più piccolo. Pensa ai caratteri come variabili per gli indirizzi email.
Tabella A:
| id | email |
|----|-------|
| 1 | a |
| 1 | b |
| 2 | c |
| 5 | e |
Tabella B
| id | email |
|----|-------|
| 3 | a |
| 3 | d |
| 4 | e |
| 4 | f |
| 3 | b |
CTE combined
:
UNISCITI di entrambe le tabelle sugli stessi indirizzi email per ottenere un punto di contatto. Gli ID degli stessi ID verranno concatenati in un 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
(scusate i nomi...):
L'obiettivo è ottenere tutti gli elementi esattamente in un solo array. In combined
puoi vedere, ad esempio, attualmente ci sono più array con l'elemento 4
:{5,4}
e {4}
.
Prima ordinando le righe in base alla lunghezza dei loro ids
array perché DISTINCT
in seguito dovrebbe richiedere l'array più lungo (perché tenendo premuto il punto di contatto {5,4}
invece di {4}
).
Quindi unnest
gli ids
array per ottenere una base per il filtraggio. Questo finisce in:
| 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 |
Dopo aver filtrato con 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 |
Siamo interessati solo agli ids
colonna con i cluster di ID univoci generati. Quindi abbiamo bisogno di tutti loro solo una volta. Questo è il lavoro dell'ultimo DISTINCT
. Quindi CTE clustered
risultati in
| ids |
|-----|
| 2 |
| 1,3 |
| 5,4 |
Ora sappiamo quali ID sono combinati e dovrebbero condividere i loro dati. Ora ci uniamo agli ids
raggruppati contro le tabelle di origine. Dal momento che lo abbiamo fatto nel CTE combined
possiamo riutilizzare questa parte (questo è il motivo per cui è esternalizzata in un unico CTE:non abbiamo più bisogno di un altro join di entrambe le tabelle in questo passaggio). L'operatore JOIN <@
dice:JOIN se l'array "touch point" di combined
è un sottogruppo del cluster id di clustered
. Ciò si traduce in:
| 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 |
Ora siamo in grado di raggruppare gli indirizzi email utilizzando gli ID raggruppati (colonna più a destra).
array_agg
aggrega le mail di una colonna, array_cat
concatena gli array e-mail di entrambe le colonne in un unico grande array e-mail.
Poiché ci sono colonne in cui l'email è NULL
possiamo filtrare questi valori prima di raggrupparli con FILTER (WHERE...)
clausola.
Risultato finora:
| array_cat |
|-----------|
| c |
| a,b,a,b,d |
| e,e,f |
Ora raggruppiamo tutti gli indirizzi e-mail per un unico ID. Dobbiamo generare nuovi ID univoci. Questo è ciò che la funzione finestra
row_number
è per. Aggiunge semplicemente un conteggio delle righe alla tabella:
| array_cat | new_id |
|-----------|--------|
| c | 1 |
| a,b,a,b,d | 2 |
| e,e,f | 3 |
L'ultimo passaggio è unnest
l'array per ottenere una riga per indirizzo email. Poiché nell'array ci sono ancora dei duplicati, possiamo eliminarli in questo passaggio con un DISTINCT
anche:
| new_id | email |
|--------|-------|
| 1 | c |
| 2 | a |
| 2 | b |
| 2 | d |
| 3 | e |
| 3 | f |