PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Postgres:agregacja kont w jedną tożsamość według wspólnego adresu e-mail

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 |


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Jak porównywać tablice w PostgreSQL

  2. jak wyświetlić pełny kod procedury składowanej?

  3. Django:NotImplementedError:annotate() + differ(fields) nie jest zaimplementowany

  4. Poprawna adnotacja JPA dla typu tekstu PostgreSQL bez adnotacji Hibernate

  5. Postgres:WSTAW, jeśli jeszcze nie istnieje