2009-02-25 23 views
4

我正在研究一個項目,這需要一個類別樹,組織爲id,父,標題表。在Postgres中,哪些是檢索類別及其子類別(以及完整樹,如果根類別的父類爲0)的最佳方法?我正在尋找一個純粹的數據庫解決方案,但是如果有一種方法可以用於Ruby和PHP--它也會很棒。PostgreSQL - 樹組織

主要目標是選擇子句的速度,原因是此表中的數據對更新/插入/刪除速度並不重要。

UPD:還會有路徑搜索,我的意思是從當前頂點(類別)到根類別的路徑。

回答

2

檢索類別及其子類別

如果你只有子項目的深度有限,您可以使用自聯接,例如,做到這一點。兩層深:

SELECT * 
FROM categories AS child 
LEFT JOIN categories AS parent ON parent.id=child.parent 
LEFT JOIN categories AS grandparent ON grandparent.id=parent.parent 
WHERE child.id=(id) OR parent.id=(id) OR grandparent.id=(id); 

不能使用標準的SQL在「父ID的外鍵」型架構的任意深度的層次做到這一點。

一些DBMS提供了非標準的分層工具,以各種方式允許這樣的事情,但是如果你想堅持跨DBMS兼容的代碼,你需要將你的模式重新調整爲更好的表示模型之一層次結構。兩種流行的是:

  • Nested Set。存儲線性排序,表示目標表的兩列中的樹的深度優先搜索(如果目標具有顯式排序,則其中一個已經具有)。

  • 。將每個祖先/後代對存儲在單獨的連接表中。

有優點和缺點每種方法,以及衆多的變體(例如,稀疏組嵌套的編號在AR,「距離」),其可以影響多種類型添加/刪除/移動位置的操作如何昂貴是。就個人而言,我傾向於默認使用簡化的嵌套集模型,因爲它包含的冗餘比AR少。

+1

您可以*通過'parent-id-foreign-key'類型模式使用標準SQL對任意深度層次結構執行此操作:您可以使用遞歸公用表表達式。無可否認,PostgreSQL從8.4開始支持這個功能,在這個線程發佈幾個月後,我認爲這可能是一個有用的附錄。 FWIW,Firebird,MS SQL Server和DB2也支持遞歸CTE,儘管MSSQL的版本有限。 Oracle有自己奇怪的語法。 – 2010-08-24 07:36:45

2

我一直在玩ltree,這是一個PostgreSQL contrib模塊,以查看它是否非常適合線程註釋。創建你的表,用於存儲路徑列並在其上創建一個ltree指數..然後,您可以執行查詢是這樣的:

ltreetest=# select path from test where path ~ '*.Astronomy.*'; 
        path      
----------------------------------------------- 
Top.Science.Astronomy 
Top.Science.Astronomy.Astrophysics 
Top.Science.Astronomy.Cosmology 
Top.Collections.Pictures.Astronomy 
Top.Collections.Pictures.Astronomy.Stars 
Top.Collections.Pictures.Astronomy.Galaxies 
Top.Collections.Pictures.Astronomy.Astronauts 

我還沒有與它周圍玩足,以確定它如何執行像插入,更新或刪除的東西。我想刪除會是什麼樣子:

DELETE FROM test WHERE path ~ '*.Astronomy.*'; 

我想,螺紋評論表可能看起來像:

CREATE SEQUENCE comment_id_seq 
    INCREMENT 1 
    MINVALUE 1 
    MAXVALUE 9223372036854775807 
    START 78616 
    CACHE 1; 

CREATE TABLE comments (
comment_id int PRIMARY KEY, 
path ltree, 
comment text 
); 

CREATE INDEX comments_path_idx ON comments USING gist (path); 

的插入會粗暴(和未經考驗的-LY)看起來像:

CREATE FUNCTION busted_add_comment(text the_comment, int parent_comment_id) RETURNS void AS 
$BODY$ 
DECLARE 
    INT _new_comment_id; -- our new comment_id 
    TEXT _parent_path; -- the parent path 
BEGIN 
    _new_comment_id := nextval('comment_id_seq'::regclass); 
    SELECT path INTO _parent_path FROM comments WHERE comment_id = parent_comment_id; 

    -- this is probably busted SQL, but you get the idea... this comment's path looks like 
    -- the.parent.path.US 
    -- 
    -- eg (if parent_comment_id was 5 and our new comment_id is 43): 
    -- 3.5.43 
    INSERT INTO comments (comment_id, comment, path) VALUES (_new_comment_id, the_comment, CONCAT(_parent_path, '.', _new_comment_id)); 

END; 
$BODY$ 
LANGUAGE 'plpgsql' VOLATILE; 

什麼的。基本上,路徑只是由所有主鍵組成的層次結構。

0

有一個用於Rails的acts_as_tree插件,它在過去對我很有幫助。儘管我有一棵相當小的樹,大約有15,000個節點。

1

我很喜歡這種情況下的嵌套集模型。更新和插入可能有點棘手,但選擇通常非常簡潔和快速。如果添加一個實際的參考節點的父的性能會更好(這將消除在某些情況下加入。它還包括的childNodes的自然排序。

當前節點和一個典型的查詢所有的孩子會是什麼樣子:

select name 
from nestedSet c inner join nestedSet p ON c.lft BETWEEN p.lft AND p.rgt 
where p.id = 1 
order by lft 

幾個精心放置group by條款也將淨你關於你的一些樹的快速統計