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

スパンフォレストの検索(再帰、PostgreSQL 9.5を使用)

    私はしばらく前に同様の質問に対する答えを書きました:無向グラフのすべての接続されたサブグラフを見つける方法 。その質問では、SQLServerを使用しました。中間CTEの詳細な説明については、その回答を参照してください。そのクエリをPostgresに適合させました。

    パスをtextに連結する代わりに、Postgres配列機能を使用してより効率的に記述できます。 列。

    WITH RECURSIVE
    CTE_Idents
    AS
    (
        SELECT old AS Ident
        FROM identities
    
        UNION
    
        SELECT new AS Ident
        FROM identities
    )
    ,CTE_Pairs
    AS
    (
        SELECT old AS Ident1, new AS Ident2
        FROM identities
        WHERE old <> new
    
        UNION
    
        SELECT new AS Ident1, old AS Ident2
        FROM identities
        WHERE old <> new
    )
    ,CTE_Recursive
    AS
    (
        SELECT
            CTE_Idents.Ident AS AnchorIdent 
            , Ident1
            , Ident2
            , ',' || Ident1 || ',' || Ident2 || ',' AS IdentPath
            , 1 AS Lvl
        FROM 
            CTE_Pairs
            INNER JOIN CTE_Idents ON CTE_Idents.Ident = CTE_Pairs.Ident1
    
        UNION ALL
    
        SELECT 
            CTE_Recursive.AnchorIdent 
            , CTE_Pairs.Ident1
            , CTE_Pairs.Ident2
            , CTE_Recursive.IdentPath || CTE_Pairs.Ident2 || ',' AS IdentPath
            , CTE_Recursive.Lvl + 1 AS Lvl
        FROM
            CTE_Pairs
            INNER JOIN CTE_Recursive ON CTE_Recursive.Ident2 = CTE_Pairs.Ident1
        WHERE
            CTE_Recursive.IdentPath NOT LIKE ('%,' || CTE_Pairs.Ident2 || ',%')
    )
    ,CTE_RecursionResult
    AS
    (
        SELECT AnchorIdent, Ident1, Ident2
        FROM CTE_Recursive
    )
    ,CTE_CleanResult
    AS
    (
        SELECT AnchorIdent, Ident1 AS Ident
        FROM CTE_RecursionResult
    
        UNION
    
        SELECT AnchorIdent, Ident2 AS Ident
        FROM CTE_RecursionResult
    )
    ,CTE_Groups
    AS
    (
      SELECT
        CTE_Idents.Ident
        ,array_agg(COALESCE(CTE_CleanResult.Ident, CTE_Idents.Ident) 
            ORDER BY COALESCE(CTE_CleanResult.Ident, CTE_Idents.Ident)) AS AllIdents
      FROM
        CTE_Idents
        LEFT JOIN CTE_CleanResult ON CTE_CleanResult.AnchorIdent = CTE_Idents.Ident
      GROUP BY CTE_Idents.Ident
    )
    SELECT AllIdents
    FROM CTE_Groups
    GROUP BY AllIdents
    ;
    

    1行(7,X,Y)を追加しました サンプルデータに。

    SQLフィドル

    結果

    |          allidents |
    |--------------------|
    |   Lydia,Mary,Nancy |
    | Albert,Bob,Charles |
    |                X,Y |
    |                Zoe |
    



    1. MySQLでコミットした後にロールバックする方法はありますか?

    2. MySQLで適切にGROUPBYする方法は?

    3. CASEを使用して複数の列を選択するMySQLクエリ

    4. OracleではDeleteステートメントが非常に遅い