2016-09-29 13 views
0

おそらく悪いタイトルです。 これは状況です:Postgresでマスタとスレーブの両方を取得するクエリ

ユーザーテーブル:

id 
name 

接続テーブル:上記のインサートを

id 
master_id 
slave_id 


insert into Users values(1,'Jack'); 
insert into Users values(2,'Marc'); 
insert into Users values(3,'Susie'); 
insert into Users values(4,'Paul'); 

insert into Connections values(1,1,2); 
insert into Connections values(2,3,1); 
insert into Connections values(3,3,4); 

、ジャックはスレーブとしてマークされています。 しかし彼はスージーの奴隷です。 Paulはスージーの奴隷です。

今、私はジャックの奴隷であるすべての人々をフェッチする必要があります。しかし、私はまた、ジャックのマスターとそのマスターの奴隷を取り出す必要があります。

英語では、ジャックのためにすべてのユーザーがテーブルに入ることを意味します。マークはジャックの奴隷だから。スージーはジャックのマスターです。 ポールはスージーの奴隷です(そして、スージーはジャックの主人だったので、ポールは何らかの形で私の所属するユーザーのリストに何らかの形で入っています)。

これはかなり明確です。

これをすべて1つのクエリで取得できますか? 今すぐ、ジャックのすべてのスレーブを取得するクエリがあります。私はジャックのすべてのマスターを得るためのものを作りました。しかし、私はすべての奴隷を得るために各マスターをループする必要があります。少なくとも3つのクエリが生成されます。 私はnode.jsをすべてのコールバックのものと併用しているので、実際には良い選択肢ではありません。 私はUNIONについて考えていましたが、私はそれが行く方法であると確信していません。また、ストアドプロシージャ、私はそれらを避けることをお勧めします。

実際に、UNIONを使用すると、Imスレーブの行とImマスタのある行の両方を選択できます。しかし、私はまだマスターがmaster_idである行をフェッチする方法を知りません。

--- EDIT ---

は、私は今、このクエリを実行しています:

select 
"connections"."master_id" 
,"connections"."slave_id" 
from 
"connections" 
where 
"connections"."master_id" = 1 
union 
select 
"connections"."master_id" 
,"connections"."slave_id" 
from 
"connections" 
where 
"connections"."slave_id" = 1  
union 
select 
"connections"."master_id" 
,"connections"."slave_id" 
from 
"connections" 
where 
"connections"."master_id" IN 
(select "connections"."master_id" from "connections" where "connections"."slave_id" = 1)   

それは私に期待される結果を与えているようです。 この場合、テーブル接続のすべての行になります。 あなたはそれが正しいようですか?

+1

あなたがそのサンプルデータから期待する出力は何ですか? –

+0

私は解決策になるかもしれないと思うことを追加しても、質問を編集しました。 – oderfla

答えて

1

これは、再帰的CTEが非常に役立つシナリオのタイプです。再帰CTEは、それ自体を参照する特別なCTEです。セルフ・ジョインを作るのではなく、階層をトラバースするためにこのメソッドを使用します。階層の深さが異なるパスや異なるパス、時間が経つにつれて、一般的には悪いオプションです。

WITH RECURSIVE recCTE() AS 
(
    /*Recursive Seed - The start of the recursive lookup*/ 
    SELECT 
     master_id as parent, 
     slave_id as child, 
     /*You can use "depth" to check how deep we are in the master/slave hierarchy*/ 
     1 as Depth, 
     /*You can use a "path" to see which people/nodes are involved in the hierarchy as it's built through the iterations of the recursive CTE*/ 
     CAST(master_id || '>' || child as VARCHAR(50)) as path 
    FROM 
     Connections 
    WHERE 
     /* here we determine who we are starting with for the lookup. You could start with everyone by omitting this*/ 
     /* We'll start with Susie */ 
     master_id = 3 


    /* 
     Recursive Term - The part of the query that refers to itself and iterates until 
     the inner join fails 
    */ 
    SELECT 
     recCTE.child as parent, 
     connections.slave_id as child, 
     recCTE.depth + 1 as depth, 
     recCTE.path || '>' || connections.slave_id as path 
    FROM 
     recCTE /*referred to itself here*/ 
     INNER JOIN connections ON 
      recCTE.child = connections.master_id /*Join child to master for next lookup of slave/child */ 
    WHERE 
     /*safe guard in case of endless cycling (A reporting to B reporting to C reporting back to A)*/ 
     recCTE.Depth < 15 

     /*besides checking for depth, you could also insure that the slave doesn't exist in the path already*/ 
     recCTE.path NOT LIKE '%' || slave_id || '%' 


) 

/*Now select from it and see what you get*/ 
SELECT * FROM recCTE; 

Check out the official Postgres documentation on Recursive CTEs here

+0

編集した質問をご覧ください。私は期待される結果を与えるような解決策を思いついた。しかし、私はその最高のものを完全には確信していません。 – oderfla

+1

あなたはうまくいくでしょう(1つのクエリで自己結合と同じように)が、階層が1つまたは2つ以上深くなると、ユニオンクエリの地獄で終わるでしょう。規模のために、あなたは再帰を考慮する必要があります。このようにしてスージーがジャックのマスターであり、マールのマスターであるマスターポールであれば、スージーのためにこれを実行し、出力でマークを得ることができます。 – JNevill

+0

よく、私はnode.jsの続編を使用していますが、 "with"がサポートされていないように見えますが、私が書いた組合が深さに関係なく常にすべての行を与えるのだろうかと思います。 – oderfla

関連する問題