demo1:db<>skrzypce , demo2:db<>skrzypce
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
Objaśnienie krok po kroku:
Dla wyjaśnienia wezmę ten zbiór danych. To jest trochę bardziej złożone niż twoje. Może lepiej zilustrować moje kroki. Niektóre problemy nie występują w twoim mniejszym zestawie. Pomyśl o znakach jako zmiennych dla adresów e-mail.
Tabela A:
| id | email |
|----|-------|
| 1 | a |
| 1 | b |
| 2 | c |
| 5 | e |
Tabela B
| id | email |
|----|-------|
| 3 | a |
| 3 | d |
| 4 | e |
| 4 | f |
| 3 | b |
CTE combined
:
POŁĄCZ obie tabele na tych samych adresach e-mail, aby uzyskać punkt kontaktu. Identyfikatory tych samych identyfikatorów zostaną połączone w jedną tablicę:
| 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
(przepraszam za nazwiska...):
Celem jest uzyskanie wszystkich elementów dokładnie w jednej tablicy. W combined
widać, na przykład obecnie istnieje więcej tablic z elementem 4
:{5,4}
i {4}
.
Najpierw uporządkowanie wierszy według długości ich ids
tablice, ponieważ DISTINCT
później powinna zająć najdłuższą tablicę (ponieważ przytrzymanie punktu kontaktu {5,4}
zamiast {4}
).
Następnie unnest
ids
tablice, aby uzyskać podstawę do filtrowania. To kończy się na:
| 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 przefiltrowaniu za 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 |
Interesują nas tylko ids
kolumna z wygenerowanymi unikalnymi klastrami identyfikatorów. Więc potrzebujemy ich wszystkich tylko raz. To jest zadanie ostatniego DISTINCT
. Więc CTE clustered
wyniki w
| ids |
|-----|
| 2 |
| 1,3 |
| 5,4 |
Teraz wiemy, które identyfikatory są połączone i powinniśmy udostępniać ich dane. Teraz dołączamy do klastrowanych ids
w stosunku do tabel pochodzenia. Ponieważ zrobiliśmy to w CTE combined
możemy ponownie wykorzystać tę część (to jest powód, dla którego jest ona przenoszona na zewnątrz do pojedynczego CTE:nie potrzebujemy już więcej łączenia obu tabel w tym kroku). Operator JOIN <@
mówi:JOIN, jeśli tablica „punktów dotykowych” combined
jest podgrupą klastra identyfikatora clustered
. Daje to w:
| 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 |
Teraz możemy pogrupować adresy e-mail przy użyciu identyfikatorów klastrowych (kolumna po prawej stronie).
array_agg
agreguje wiadomości e-mail z jednej kolumny, array_cat
łączy tablice e-mail obu kolumn w jedną dużą tablicę e-mail.
Ponieważ istnieją kolumny, w których adres e-mail jest NULL
możemy odfiltrować te wartości przed klastrowaniem za pomocą FILTER (WHERE...)
klauzula.
Dotychczasowy wynik:
| array_cat |
|-----------|
| c |
| a,b,a,b,d |
| e,e,f |
Teraz grupujemy wszystkie adresy e-mail pod jednym identyfikatorem. Musimy wygenerować nowe unikalne identyfikatory. To właśnie funkcja okna
row_number
jest dla. Po prostu dodaje liczbę wierszy do tabeli:
| array_cat | new_id |
|-----------|--------|
| c | 1 |
| a,b,a,b,d | 2 |
| e,e,f | 3 |
Ostatnim krokiem jest unnest
tablica, aby uzyskać wiersz na adres e-mail. Ponieważ w tablicy nadal znajdują się niektóre duplikaty, możemy je wyeliminować w tym kroku za pomocą DISTINCT
a także:
| new_id | email |
|--------|-------|
| 1 | c |
| 2 | a |
| 2 | b |
| 2 | d |
| 3 | e |
| 3 | f |