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
ステップバイステップの説明:
説明のために、このデータセットを取り上げます。これはあなたのものより少し複雑です。それは私のステップをよりよく説明することができます。小さいセットでは発生しない問題がいくつかあります。文字をメールアドレスの変数と考えてください。
表A:
| id | email |
|----|-------|
| 1 | a |
| 1 | b |
| 2 | c |
| 5 | e |
表B
| id | email |
|----|-------|
| 3 | a |
| 3 | d |
| 4 | e |
| 4 | f |
| 3 | b |
CTEcombined
:
タッチポイントを取得するには、同じ電子メールアドレスで両方のテーブルを結合します。同じIDのIDが1つの配列に連結されます:
| 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 |
CTEclustered
(名前は申し訳ありません...):
目標は、すべての要素を1つの配列だけで正確に取得することです。 combined
たとえば、現在、要素4
を持つ配列がさらにあります。 :{5,4}
および{4}
。
まず、ids
の長さで行を並べ替えます DISTINCT
のため、配列 後で最も長い配列を取る必要があります(タッチポイント{5,4}
を保持しているため) {4}
の代わりに 。
次に、unnest
ids
フィルタリングの基礎を取得するための配列。これは次のように終わります:
| 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 |
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 |
ids
にのみ関心があります 生成された一意のIDクラスターを持つ列。したがって、それらすべてが必要になるのは1回だけです。これは最後のDISTINCT
の仕事です 。したがって、CTEclustered
結果
| ids |
|-----|
| 2 |
| 1,3 |
| 5,4 |
これで、どのIDが組み合わされ、それらのデータを共有する必要があるかがわかりました。次に、クラスター化されたids
に参加します オリジンテーブルに対して。これはCTEのcombined
で行ったので この部分を再利用できます(ちなみに、これが単一のCTEにアウトソーシングされている理由です。このステップで両方のテーブルを再度結合する必要はありません)。 JOIN演算子<@
言う:combined
の「タッチポイント」配列の場合は参加する clustered
のidクラスターのサブグループです 。これにより、次の結果が得られます:
| 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 |
これで、クラスター化されたID(右端の列)を使用してメールアドレスをグループ化できるようになりました。
array_agg
array_cat
という1つの列のメールを集約します 両方の列の電子メール配列を1つの大きな電子メール配列に連結します。
メールがNULL
である列があるため FILTER (WHERE...)
でクラスタリングする前に、これらの値をフィルターで除外できます。 条項。
これまでの結果:
| array_cat |
|-----------|
| c |
| a,b,a,b,d |
| e,e,f |
ここで、すべての電子メールアドレスを1つのIDにグループ化します。新しい一意のIDを生成する必要があります。それがrow_number
です。テーブルに行数を追加するだけです:
| array_cat | new_id |
|-----------|--------|
| c | 1 |
| a,b,a,b,d | 2 |
| e,e,f | 3 |
最後のステップは、unnest
することです 電子メールアドレスごとに行を取得する配列。配列にはまだいくつかの重複があるため、このステップでDISTINCT
を使用してそれらを排除できます。 同様に:
| new_id | email |
|--------|-------|
| 1 | c |
| 2 | a |
| 2 | b |
| 2 | d |
| 3 | e |
| 3 | f |