sql >> Database teknologi >  >> RDS >> PostgreSQL

Postgres:Saml konti til en enkelt identitet ved hjælp af fælles e-mailadresse

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
 

Trin for trin forklaring:

Til forklaring tager jeg dette datasæt. Det her er lidt mere komplekst end dit. Det kan illustrere mine trin bedre. Nogle problemer opstår ikke i dit mindre sæt. Tænk på tegnene som variabler for e-mail-adresser.

Tabel A:

| id | email | |----|-------| | 1 | a | | 1 | b | | 2 | c | | 5 | e |

Tabel B

| id | email | |----|-------| | 3 | a | | 3 | d | | 4 | e | | 4 | f | | 3 | b |

CTE combined :

JOIN af begge borde på samme e-mailadresser for at få et kontaktpunkt. ID'er af samme Id'er vil blive sammenkædet i ét 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 (undskyld navnene...):

Målet er at få alle elementer nøjagtigt i kun ét array. I combined du kan se, for eksempel i øjeblikket er der flere arrays med elementet 4 :{5,4} og {4} .

Først sorteres rækkerne efter længden af ​​deres ids arrays, fordi DISTINCT senere skulle tage det længste array (fordi man holder berøringspunktet {5,4} i stedet for {4} ).

Derefter unnest ids arrays for at få et grundlag for filtrering. Dette ender med:

| 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 |

Efter filtrering med 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 |

Vi er kun interesserede i ids kolonne med de genererede unikke id-klynger. Så vi har kun brug for dem alle én gang. Dette er opgaven for den sidste DISTINCT . Så CTE clustered resulterer i

| ids | |-----| | 2 | | 1,3 | | 5,4 |

Nu ved vi, hvilke id'er der er kombineret og bør dele deres data. Nu slutter vi os til de klyngede ids mod oprindelsestabellerne. Da vi har gjort dette i CTE combined vi kan genbruge denne del (det er i øvrigt grunden til, at den er outsourcet til en enkelt CTE:Vi har ikke længere brug for endnu en sammenføjning af begge tabeller i dette trin). JOIN-operatøren <@ siger:JOIN hvis "touch point"-arrayet af combined er en undergruppe af id-klyngen af ​​clustered . Dette giver:

| 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 |

Nu er vi i stand til at gruppere e-mail-adresserne ved at bruge de klyngede id'er (kolonne længst til højre).

array_agg samler mails fra én kolonne, array_cat sammenkæder e-mail-arrays af begge kolonner til en stor e-mail-array.

Da der er kolonner, hvor e-mail er NULL vi kan filtrere disse værdier fra før klyngedannelse med FILTER (WHERE...) klausul.

Resultat indtil videre:

| array_cat | |-----------| | c | | a,b,a,b,d | | e,e,f |

Nu grupperer vi alle e-mailadresser til ét enkelt id. Vi skal generere nye unikke id'er. Det er hvad vinduefunktionen row_number er for. Det tilføjer simpelthen et rækkeantal til tabellen:

| array_cat | new_id | |-----------|--------| | c | 1 | | a,b,a,b,d | 2 | | e,e,f | 3 |

Sidste trin er at unnest arrayet for at få en række pr. e-mailadresse. Da der stadig er nogle dubletter i arrayet, kan vi fjerne dem i dette trin med en DISTINCT også:

| new_id | email | |--------|-------| | 1 | c | | 2 | a | | 2 | b | | 2 | d | | 3 | e | | 3 | f |

  1. Oracle vælg gensidig understreng

  2. Mysql rekursiv subtrahering og multiplikation af grupperede værdier

  3. ConnectionString til at forbinde 3 master noder til MySQL

  4. Opdater felt, når ikke null