PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Postgres:Tổng hợp các tài khoản thành một danh tính duy nhất theo địa chỉ email chung

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

Giải thích từng bước:

Để giải thích, tôi sẽ lấy tập dữ liệu này. Điều này phức tạp hơn một chút so với của bạn. Nó có thể minh họa các bước của tôi tốt hơn. Một số vấn đề không xảy ra trong tập hợp nhỏ hơn của bạn. Hãy nghĩ về các ký tự như các biến cho địa chỉ email.

Bảng A:

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

Bảng B

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

CTE combined :

THAM GIA cả hai bảng trên cùng một địa chỉ email để có được điểm tiếp xúc. Các ID của cùng một Id sẽ được nối trong một mảng:

|   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 (xin lỗi vì những cái tên ...):

Mục tiêu là lấy chính xác tất cả các phần tử chỉ trong một mảng. Trong combined bạn có thể thấy, ví dụ như hiện tại có nhiều mảng hơn với phần tử 4 :{5,4}{4} .

Đầu tiên sắp xếp các hàng theo độ dài của ids của chúng mảng vì DISTINCT sau đó sẽ chiếm mảng dài nhất (vì giữ điểm tiếp xúc {5,4} thay vì {4} ).

Sau đó, unnest ids mảng để lấy cơ sở lọc. Điều này kết thúc bằng:

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

Sau khi lọc bằng 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 |

Chúng tôi chỉ quan tâm đến ids với các cụm id duy nhất được tạo. Vì vậy, chúng tôi cần tất cả chúng chỉ một lần. Đây là công việc của DISTINCT cuối cùng . Vì vậy, CTE clustered kết quả trong

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

Bây giờ chúng tôi biết id nào được kết hợp và nên chia sẻ dữ liệu của chúng. Bây giờ chúng ta tham gia các ids được nhóm lại so với các bảng gốc. Vì chúng tôi đã thực hiện việc này trong CTE combined chúng ta có thể sử dụng lại phần này (đó là lý do tại sao nó được thuê ngoài thành một CTE duy nhất bằng cách:Chúng ta không cần một phép nối khác của cả hai bảng trong bước này nữa). Toán tử JOIN <@ nói:THAM GIA nếu mảng "điểm tiếp xúc" của combined là một nhóm con của cụm id của clustered . Điều này dẫn đến:

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

Giờ đây, chúng tôi có thể nhóm các địa chỉ email bằng cách sử dụng id nhóm (cột ngoài cùng bên phải).

array_agg tổng hợp các thư của một cột, array_cat nối các mảng email của cả hai cột thành một mảng email lớn.

Vì có các cột trong đó email là NULL chúng ta có thể lọc các giá trị này trước khi phân cụm với FILTER (WHERE...) mệnh đề.

Kết quả cho đến nay:

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

Bây giờ chúng tôi nhóm tất cả các địa chỉ email cho một id duy nhất. Chúng tôi phải tạo id duy nhất mới. Đó là chức năng của cửa sổ row_number là cho. Nó chỉ đơn giản là thêm một số hàng vào bảng:

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

Bước cuối cùng là unnest mảng để lấy một hàng cho mỗi địa chỉ email. Vì trong mảng vẫn còn một số bản sao, chúng ta có thể loại bỏ chúng trong bước này bằng DISTINCT nữa:

| 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. Rake spec thất bại trong hầu hết các bài kiểm tra, nhưng rails vẫn hoạt động tốt trên nguồn Diaspora

  2. Có an toàn để sắp xếp lại các cột trong schema.rb cho Rails 4 / Postgres không?

  3. pgp Dự đoán - Phân tích dự đoán trong PostgreSQL

  4. Postgis + boost ::hình học + C ++

  5. Làm cách nào để lưu trữ mã HTML trong trường bảng Postgresql DB?