PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Postgres:Gabungkan akun menjadi satu identitas dengan alamat email yang sama

demo1:db<>biola , demo2:db<>biola

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

Penjelasan langkah demi langkah:

Untuk penjelasan saya akan mengambil dataset ini. Ini sedikit lebih rumit daripada milik Anda. Itu bisa menggambarkan langkah saya dengan lebih baik. Beberapa masalah tidak terjadi di perangkat Anda yang lebih kecil. Pikirkan karakter sebagai variabel untuk alamat email.

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 :

GABUNG kedua tabel pada alamat email yang sama untuk mendapatkan titik kontak. ID dari Id yang sama akan digabungkan dalam satu larik:

|   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 (maaf atas nama-namanya...):

Tujuannya adalah untuk mendapatkan semua elemen tepat hanya dalam satu array. Dalam combined Anda bisa lihat, misalnya saat ini ada lebih banyak array dengan elemen 4 :{5,4} dan {4} .

Pertama-tama urutkan baris berdasarkan panjang ids their array karena DISTINCT nanti harus mengambil larik terpanjang (karena memegang titik sentuh {5,4} bukannya {4} ).

Kemudian unnest ids array untuk mendapatkan dasar untuk penyaringan. Ini berakhir dengan:

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

Setelah memfilter dengan 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 |

Kami hanya tertarik pada ids kolom dengan cluster id unik yang dihasilkan. Jadi kita membutuhkan semuanya hanya sekali. Ini adalah tugas DISTINCT terakhir . Jadi CTE clustered hasil dalam

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

Sekarang kita tahu id mana yang digabungkan dan harus membagikan datanya. Sekarang kita bergabung dengan ids yang berkerumun terhadap tabel asal. Karena kami telah melakukan ini dalam combined CTE kita dapat menggunakan kembali bagian ini (itulah alasan mengapa bagian ini di-outsource menjadi satu CTE:Kita tidak perlu lagi menggabungkan kedua tabel dalam langkah ini). Operator GABUNG <@ mengatakan:GABUNG jika array "titik sentuh" ​​dari combined adalah subgrup dari kluster id dari clustered . Ini menghasilkan:

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

Sekarang kita dapat mengelompokkan alamat email dengan menggunakan clustered id (kolom paling kanan).

array_agg menggabungkan surat-surat dari satu kolom, array_cat menggabungkan larik email dari kedua kolom menjadi satu larik email besar.

Karena ada kolom di mana email adalah NULL kita dapat memfilter nilai-nilai ini sebelum mengelompokkan dengan FILTER (WHERE...) klausa.

Hasil sejauh ini:

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

Sekarang kami mengelompokkan semua alamat email untuk satu id tunggal. Kita harus membuat id unik baru. Itulah fungsi jendela row_number adalah untuk. Itu hanya menambahkan jumlah baris ke tabel:

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

Langkah terakhir adalah unnest array untuk mendapatkan baris per alamat email. Karena dalam array masih ada beberapa duplikat, kita dapat menghilangkannya pada langkah ini dengan DISTINCT juga:

| 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. DecimalField Mengonversi Nol ke 0E-10

  2. Kesalahan soket Postgresql saat menjalankan syncdb Django (mac OS 10.7.5)

  3. Postgres:Perluas kolom JSON menjadi baris

  4. Stempel waktu jOOQ disimpan dengan offset Zona Waktu lokal

  5. Beri tahu dari pemicu di tabel yang direplikasi pglogical