sql >> Databáze >  >> RDS >> PostgreSQL

Postgres:Sloučení účtů do jedné identity pomocí společné e-mailové adresy

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

Vysvětlení krok za krokem:

Pro vysvětlení vezmu tento datový soubor. Tohle je trochu složitější než to tvoje. Může lépe ilustrovat mé kroky. Některé problémy se ve vaší menší sadě nevyskytují. Přemýšlejte o znacích jako o proměnných pro e-mailové adresy.

Tabulka A:

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

Tabulka B

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

CTE combined :

PŘIPOJTE se k oběma stolům na stejné e-mailové adrese, abyste získali kontaktní bod. ID se stejnými ID budou zřetězena do jednoho pole:

|   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 (omlouvám se za jména...):

Cílem je dostat všechny prvky přesně do jednoho pole. V combined můžete vidět, například aktuálně existuje více polí s prvkem 4 :{5,4} a {4} .

Nejprve seřaďte řádky podle délky jejich ids pole, protože DISTINCT později by mělo trvat nejdelší pole (protože držení dotykového bodu {5,4} místo {4} ).

Poté unnest ids polí získat základ pro filtrování. Toto končí:

| 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 filtrování 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 |

Zajímají nás pouze ids sloupec s vygenerovanými jedinečnými id clustery. Všechny je tedy potřebujeme jen jednou. Toto je úloha posledního DISTINCT . Takže CTE clustered výsledky v

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

Nyní víme, která ID jsou kombinována a měli bychom sdílet svá data. Nyní připojíme seskupená ids proti tabulkám původu. Protože jsme to udělali v CTE combined můžeme tuto část znovu použít (to je mimochodem důvod, proč je outsourcována do jediného CTE:V tomto kroku již nepotřebujeme další spojení obou tabulek). Operátor JOIN <@ říká:JOIN, pokud je pole "touch point" combined je podskupinou id clusteru clustered . Výsledkem je:

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

Nyní jsme schopni seskupit e-mailové adresy pomocí seskupených ID (sloupec zcela vpravo).

array_agg agreguje e-maily jednoho sloupce, array_cat zřetězí pole e-mailů obou sloupců do jednoho velkého pole e-mailů.

Protože existují sloupce, kde je email NULL tyto hodnoty můžeme před shlukováním odfiltrovat pomocí FILTER (WHERE...) klauzule.

Dosavadní výsledek:

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

Nyní seskupujeme všechny e-mailové adresy pro jedno jediné ID. Musíme vygenerovat nová unikátní ID. To je to, co funkce okna row_number je pro. Jednoduše přidá počet řádků do tabulky:

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

Posledním krokem je unnest pole získat řádek na e-mailovou adresu. Protože v poli jsou stále nějaké duplikáty, můžeme je v tomto kroku odstranit pomocí DISTINCT také:

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


  1. Podmíněné poskytování loga na vzdáleném webu

  2. Dopad definice sloupce VARCHAR2 s větší délkou

  3. Jak mohu zabránit opakovaným automatickým připojením k databázi Oracle?

  4. Optimistická souběžnost:IsConcurrencyToken a RowVersion