sql >> Database >  >> RDS >> PostgreSQL

Postgres:accounts samenvoegen tot één identiteit op gemeenschappelijk e-mailadres

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

Stap voor stap uitleg:

Voor uitleg neem ik deze dataset. Dit is een beetje ingewikkelder dan de jouwe. Het kan mijn stappen beter illustreren. Sommige problemen doen zich niet voor in uw kleinere set. Beschouw de tekens als variabelen voor e-mailadressen.

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 van beide tabellen op dezelfde e-mailadressen om een ​​contactpunt te krijgen. ID's van dezelfde ID's worden samengevoegd in één 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 (sorry voor de namen...):

Het doel is om alle elementen precies in één array te krijgen. In combined je kunt zien dat er momenteel bijvoorbeeld meer arrays zijn met het element 4 :{5,4} en {4} .

Sorteer eerst de rijen op de lengte van hun ids arrays omdat de DISTINCT later zou de langste array moeten duren (omdat het aanraken van het aanraakpunt {5,4} in plaats van {4} ).

Dan unnest de ids arrays om een ​​basis voor het filteren te krijgen. Dit eindigt op:

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

Na filteren met 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 |

We zijn alleen geïnteresseerd in de ids kolom met de gegenereerde unieke id-clusters. We hebben ze dus allemaal maar één keer nodig. Dit is de taak van de laatste DISTINCT . Dus CTE clustered resulteert in

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

Nu weten we welke ID's worden gecombineerd en hun gegevens moeten delen. Nu voegen we ons bij de geclusterde ids tegen de oorsprongstabellen. Aangezien we dit hebben gedaan in de CTE combined we kunnen dit onderdeel hergebruiken (dat is trouwens de reden waarom het is uitbesteed aan een enkele CTE:we hebben in deze stap geen nieuwe samenvoeging van beide tabellen meer nodig). De JOIN-operator <@ zegt:JOIN als de "touch point" array van combined is een subgroep van het id-cluster van clustered . Dit levert op 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 |

Nu kunnen we de e-mailadressen groeperen met behulp van de geclusterde ID's (meest rechtse kolom).

array_agg verzamelt de e-mails van één kolom, array_cat voegt de e-mailarrays van beide kolommen samen tot één grote e-mailarray.

Aangezien er kolommen zijn waar e-mail NULL is we kunnen deze waarden eruit filteren voordat ze worden geclusterd met de FILTER (WHERE...) clausule.

Resultaat tot nu toe:

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

Nu groeperen we alle e-mailadressen voor één enkele id. We moeten nieuwe unieke id's genereren. Dat is wat de vensterfunctie row_number is voor. Het voegt gewoon een rijtelling toe aan de tabel:

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

De laatste stap is het unnest de array om een ​​rij per e-mailadres te krijgen. Aangezien er in de array nog enkele duplicaten zijn, kunnen we deze in deze stap elimineren met een DISTINCT ook:

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


  1. CSV laden in MySQL Is dit een bug? - Uitzondering bestand niet gevonden

  2. Strip de laatste twee tekens van een kolom in MySQL

  3. Een identiteit toevoegen aan een bestaande kolom

  4. Twee externe sleutels in plaats van primaire