我正在研究一個項目,這需要一個類別樹,組織爲id,父,標題表。在Postgres中,哪些是檢索類別及其子類別(以及完整樹,如果根類別的父類爲0)的最佳方法?我正在尋找一個純粹的數據庫解決方案,但是如果有一種方法可以用於Ruby和PHP--它也會很棒。PostgreSQL - 樹組織
主要目標是選擇子句的速度,原因是此表中的數據對更新/插入/刪除速度並不重要。
UPD:還會有路徑搜索,我的意思是從當前頂點(類別)到根類別的路徑。
我正在研究一個項目,這需要一個類別樹,組織爲id,父,標題表。在Postgres中,哪些是檢索類別及其子類別(以及完整樹,如果根類別的父類爲0)的最佳方法?我正在尋找一個純粹的數據庫解決方案,但是如果有一種方法可以用於Ruby和PHP--它也會很棒。PostgreSQL - 樹組織
主要目標是選擇子句的速度,原因是此表中的數據對更新/插入/刪除速度並不重要。
UPD:還會有路徑搜索,我的意思是從當前頂點(類別)到根類別的路徑。
檢索類別及其子類別
如果你只有子項目的深度有限,您可以使用自聯接,例如,做到這一點。兩層深:
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少。
查看"ltree" contrib模塊。
我一直在玩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;
什麼的。基本上,路徑只是由所有主鍵組成的層次結構。
有一個用於Rails的acts_as_tree
插件,它在過去對我很有幫助。儘管我有一棵相當小的樹,大約有15,000個節點。
我很喜歡這種情況下的嵌套集模型。更新和插入可能有點棘手,但選擇通常非常簡潔和快速。如果添加一個實際的參考節點的父的性能會更好(這將消除在某些情況下加入。它還包括的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
條款也將淨你關於你的一些樹的快速統計
只需添加,文章Managing Hierarchical Data in MySQL就可以很好地解釋相鄰列表模型和嵌套集合模型,包括樹操作等的示例SQL。
RDBMS中的層次結構是一個難題。我在我的願望清單上有Joe Celko’s Trees and Hierarchies in SQL for Smarties可以在某天購買和閱讀。
您可以*通過'parent-id-foreign-key'類型模式使用標準SQL對任意深度層次結構執行此操作:您可以使用遞歸公用表表達式。無可否認,PostgreSQL從8.4開始支持這個功能,在這個線程發佈幾個月後,我認爲這可能是一個有用的附錄。 FWIW,Firebird,MS SQL Server和DB2也支持遞歸CTE,儘管MSSQL的版本有限。 Oracle有自己奇怪的語法。 – 2010-08-24 07:36:45