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}
và {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 |