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) | example@sqldat.com | 3 |
| example@sqldat.com | example@sqldat.com | 1,3 |
| example@sqldat.com | (null) | 1 |
| example@sqldat.com | (null) | 2 |
| (null) | example@sqldat.com | 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 |