2016-09-29 116 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); 

利用上述的插入,傑克有馬克作爲從站。 但他也是蘇茜的奴隸。甚至保羅是蘇茜的奴隸。

現在我需要抓取所有傑克奴隸的人。但我也需要取傑克的主人和那個主人的奴隸。

英語那豈不是傑克,我會得到表中的所有用戶。由於馬克是傑克的奴隸。蘇茜是傑克的主人。 保羅是蘇茜的奴隸(蘇茜是傑克的主人,所以保羅在某種程度上屬於我以某種方式屬於我的用戶列表)。

希望這很清楚。

是否有可能在一個查詢中得到所有這一切? 現在有一個查詢取得所有傑克的奴隸。我建立了一個讓所有傑克的主人。但是,我需要循環每個主人以獲得他的所有奴隸。所有這些都會生成至少3個查詢。 由於我使用node.js和所有的回調函數,它並不是一個好的選擇。 我在想聯盟,但林不知道這是要走的路。而且還有存儲過程,我寧願避免它們。

其實用UNION我可以同時選擇的行,其中林從和那些林主。但我仍然不知道如何獲取主人master_id所在的行。

---編輯---

我現在運行此查詢:

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或2,你將最終得到一個聯合查詢的地獄。爲了規模,你應該考慮遞歸。這樣,如果蘇西是傑克的主人,而傑克是主人馬克的主人,那麼你可以爲蘇西運行這個程序,並將馬克輸出。 – JNevill

+0

以及即時使用sequelize for node.js,因爲它似乎「與」不支持,然後我不知道我寫的工會總是會給所有的行,無論深度。 – oderfla