postgresql – 使用Query遞迴查詢父
我正在使用postgresql.我的桌子如下所示
parent_idchild_id ---------------------- 101102 103104 104105 105106
我想寫一個sql查詢,它將給出輸入的最終父級.
假設我以106作為輸入,則其輸出為103.
(106 --> 105 --> 104 --> 103)
這是一個完整的例子.首先是DDL:
test=> CREATE TABLE node ( test(>id SERIAL, test(>label TEXT NOT NULL, -- name of the node test(>parent_id INT, test(>PRIMARY KEY(id) test(> ); NOTICE:CREATE TABLE will create implicit sequence "node_id_seq" for serial column "node.id" NOTICE:CREATE TABLE / PRIMARY KEY will create implicit index "node_pkey" for table "node" CREATE TABLE
…和一些資料…
test=> INSERT INTO node (label, parent_id) VALUES ('n1',NULL),('n2',1),('n3',2),('n4',3); INSERT 0 4 test=> INSERT INTO node (label) VALUES ('garbage1'),('garbage2'), ('garbage3'); INSERT 0 3 test=> INSERT INTO node (label,parent_id) VALUES ('garbage4',6); INSERT 0 1 test=> SELECT * FROM node; id |label| parent_id ----+----------+----------- 1 | n1| 2 | n2|1 3 | n3|2 4 | n4|3 5 | garbage1 | 6 | garbage2 | 7 | garbage3 | 8 | garbage4 |6 (8 rows)
這對節點中的每個id執行遞迴查詢:
test=> WITH RECURSIVE nodes_cte(id, label, parent_id, depth, path) AS ( SELECT tn.id, tn.label, tn.parent_id, 1::INT AS depth, tn.id::TEXT AS path FROM node AS tn WHERE tn.parent_id IS NULL UIO/">NION ALL SELECT c.id, c.label, c.parent_id, p.depth + 1 AS depth, (p.path || '->' || c.id::TEXT) FROM nodes_cte AS p, node AS c WHERE c.parent_id = p.id ) SELECT * FROM nodes_cte AS n ORDER BY n.id ASC; id |label| parent_id | depth |path ----+----------+-----------+-------+------------ 1 | n1||1 | 1 2 | n2|1 |2 | 1->2 3 | n3|2 |3 | 1->2->3 4 | n4|3 |4 | 1->2->3->4 5 | garbage1 ||1 | 5 6 | garbage2 ||1 | 6 7 | garbage3 ||1 | 7 8 | garbage4 |6 |2 | 6->8 (8 rows)
這得到所有後代WHERE node.id = 1:
test=> WITH RECURSIVE nodes_cte(id, label, parent_id, depth, path) AS ( SELECT tn.id, tn.label, tn.parent_id, 1::INT AS depth, tn.id::TEXT AS path FROM node AS tn WHERE tn.id = 1 UNION ALL SELECT c.id, c.label, c.parent_id, p.depth + 1 AS depth, (p.path || '->' || c.id::TEXT) FROM nodes_cte AS p, node AS c WHERE c.parent_id = p.id ) SELECT * FROM nodes_cte AS n; id | label | parent_id | depth |path ----+-------+-----------+-------+------------ 1 | n1||1 | 1 2 | n2|1 |2 | 1->2 3 | n3|2 |3 | 1->2->3 4 | n4|3 |4 | 1->2->3->4 (4 rows)
以下將獲取節點的ID為4的路徑:
test=> WITH RECURSIVE nodes_cte(id, label, parent_id, depth, path) AS ( SELECT tn.id, tn.label, tn.parent_id, 1::INT AS depth, tn.id::TEXT AS path FROM node AS tn WHERE tn.parent_id IS NULL UNION ALL SELECT c.id, c.label, c.parent_id, p.depth + 1 AS depth, (p.path || '->' || c.id::TEXT) FROM nodes_cte AS p, node AS c WHERE c.parent_id = p.id ) SELECT * FROM nodes_cte AS n WHERE n.id = 4; id | label | parent_id | depth |path ----+-------+-----------+-------+------------ 4 | n4|3 |4 | 1->2->3->4 (1 row)
讓我們假設你想限制搜尋到深度小於3的後代(請注意,深度還沒有增加):
test=> WITH RECURSIVE nodes_cte(id, label, parent_id, depth, path) AS ( SELECT tn.id, tn.label, tn.parent_id, 1::INT AS depth, tn.id::TEXT AS path FROM node AS tn WHERE tn.id = 1 UNION ALL SELECT c.id, c.label, c.parent_id, p.depth + 1 AS depth, (p.path || '->' || c.id::TEXT) FROM nodes_cte AS p, node AS c WHERE c.parent_id = p.id AND p.depth < 2 ) SELECT * FROM nodes_cte AS n; id | label | parent_id | depth | path ----+-------+-----------+-------+------ 1 | n1||1 | 1 2 | n2|1 |2 | 1->2 (2 rows)
我建議使用ARRAY資料型別而不是用於演示“路徑”的字串,但箭頭更多地說明了父<=>子關係.
程式碼日誌版權宣告:
翻譯自:http://stackoverflow.com/questions/3699395/find-parent-recursively-using-query