sql >> データベース >  >> RDS >> PostgreSQL

Postgres:共通のメールアドレスでアカウントを単一のIDに集約します

    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 |
    


    1. PostgreSQLの日付から週番号を取得する

    2. MySqlクエリの結果は1日中

    3. phppdoはmysqlから1つの値のみを取得します。変数に等しい値

    4. 月別のLaravelカーボングループ