2017-10-20 15 views
1

私はこの問題の解決策を探して少しクレイジーだ:Postgres。どのように子供の基準を満たすすべての親を得るには?

私は、この表のようなものだ:

表データ

enter image description here

そして私は、クエリがすべて取得したいの条件とすべての親を渡す要素は、私は、この結果を意味します:

クエリ結果

enter image description here

私は、クエリに考えてきた:

SELECT a.* FROM table a 
    JOIN table b ON b.id = a.id 
    WHERE a.id IN (SELECT DISTINCT c.parent_id FROM table c WHERE c.condition = TRUE) 
    OR b.id IN (SELECT DISTINCT c.id FROM table c WHERE c.condition = TRUE); 

しかし、私はこの方法だけで違いの1つのレベルを取得することができ、私は意味、私はせずに1人の以上の親を取得することはできません調子。 ありがとうございます。

答えて

2

あなたはこのためrecursive CTEを使用することができます。

WITH RECURSIVE recCTE AS 
(
    /*Get all the true children to seed the recursive query*/ 
    SELECT 
     id, 
     parent_id, 
     condition as initial_condition, 
     1 as depth, 
     CAST(id as varchar(50)) as path 
    FROM 
     table a 
    WHERE 
     a.id NOT IN (SELECT DISTINCT parent_id from table) 
     and a.condition = 'true' 

    UNION ALL 

    /*Recursive bit that refers back to itself. Find the parents*/ 
    SELECT 
     b.id, 
     b.parent_id, 
     a.initial_condition, 
     depth + 1 as depth, 
     cast(path || '>' || b.id as varchar(50)) as path   

    FROM 
     recCTE a 
     INNER JOIN table b ON 
      a.parent_id = b.id 
    WHERE 
     /*avoid going too deep in case of cycling*/ 
     depth <= 20 
) 
SELECT * FROM recCTE 

再帰CTEは二つの部分を使用しています:

  1. 再帰種子:これは、UNIONクエリの最初の半分です。これで、「True」であるすべての子(IDがParent_IDでない)を特定します。

  2. 再帰的な用語:これはUNIONクエリの後半です。それはFROM句の中で自身(recCTE)を参照し、tableに再び参加します。 recCTE.parent_id(以前の反復parent_id)をテーブルのidにリンクします。次に、その反復に必要なすべての情報を取得します。

私はほとんどの場合、この階層の他のノードは、我々はに到達するためにヒットでした再帰的な深さ(それはこのレコードを取得するためにかかりましたどのように多くの再帰)、そして一番下の子供から始まるパスを(追跡このレコード)。

私はウサギの穴をあまりにも遠くに通り過ぎることがないように、深さを使っています。イベントでは次のようなレコードを持っている:無限ループ(循環)最悪のシナリオは、それが深い20サイクルになった後、それが停止しますですが引き起こす

+----+-----------+ 
| id | parent_id | 
+----+-----------+ 
| 1 |   5 | 
| 5 |   7 | 
| 7 |   1 | 
+----+-----------+ 

を(1> 5> 7> 1> 5> 7> 1> 5> 7> 1> 5> 7> 1> 5> 7> 1> 5> 7> 1> 5)。サイクリングを停止するには、パスフィールドを使用するなど、他の方法があります(例:WHERE a.path NOT LIKE '%' || a.parent_id || '%')。

必要に応じて、最終的な選択肢が少し好きになるかもしれませんが、そこには95%の道があります。

+0

うわー、本当にいい説明、私はこれで夢中になりました、私は再帰的なCTEについて少し読んで驚きです! あなたのおかげで、その日を完全に保存しました。私は自分のテーブルにクエリを適用し、うまくいきました:D –

+0

素晴らしい!私はそれが箱の外であなたのために働いてうれしいです。 Recursive CTEの学習曲線は間違いありませんが、あなたの周りに頭を浮かべれば、彼らはツールボックスにとって完璧な意味合いと素晴らしいツールになります。 – JNevill

関連する問題