2010-02-28 105 views
2

這是一段時間一直困擾着我的心理鍛鍊。你會用什麼策略來解決這類問題?遞歸子目錄SQL問題

我們來考慮下面簡單的數據庫結構。我們有目錄,顯然是他們的一棵樹。我們也有內容項目,它們總是駐留在某些目錄中。

create table directory ( 
directoryId integer generated always as identity primary key, 
parentId integer default null, 
directoryName varchar(100) 
); 

create table content (
contentId integer generated always as identity primary key, 
directory integer references directory(directoryId), 
contentTitle varchar(100), 
contentText varchar(32000) 
); 

現在讓我們假設我們的目錄樹很大,內容量也很大。解決方案必須很好地擴展。

主要問題:如何高效地檢索從指定目錄及其子目錄中找到的所有內容項目?

我看到它的方式SQL不能用於簡化所有的子查詢的directoryIds。我對麼?

人們可以用簡單的遞歸循環來解決這個問題。這可能會變得非常沉重,並且需要棘手的緩存,特別是要保證合理的首次訪問時間。

也可以建立一個物化查詢表併爲其動態添加多維索引。可能的,但實施混亂。太複雜。

我迄今爲止最喜歡的解決方案是可能添加新的表像

create table subdirectories (
directoryId integer, 
subdirectoryId integer, 
constraint thekey primary key (directoryId,subdirectoryId) 
) 

,並確保我總是手動更新它正在移動目錄時/刪除/創建。因此,我總是可以使用directoryId進行選擇,併爲子目錄獲取所有的ID,包括作爲更復雜查詢的子查詢。我也喜歡rdbms能夠很好地優化查詢的事實。

你們認爲什麼?

+0

爲什麼要結束投票?這是一個完全有效的問題。 – Quassnoi 2010-02-28 20:19:33

回答

4

SQL Server 2005PostgreSQL 8.4Oracle 11g

WITH  
     -- uncomment the next line in PostgreSQL 
     -- RECURSIVE 
     q AS 
     (
     SELECT directoryId 
     FROM directories 
     WHERE directoryId = 1 
     UNION ALL 
     SELECT d.directoryId 
     FROM q 
     JOIN directories 
     WHERE parentId = q.directoryId 
     ) 
SELECT c.* 
FROM q 
JOIN content c 
ON  c.directory = q.directoryId 

Oracle11g

SELECT c.* 
FROM (
     SELECT directoryId 
     FROM directories 
     START WITH 
       directoryId = 1 
     CONNECT BY 
       parent = PRIOR directoryID 
     ) q 
JOIN content c 
ON  c.directory = q.directoryId 

PostgreSQL 8.3和下面看到的這篇文章:

MySQL,看到這篇文章:

+0

Oooh。這非常好! – utteputtes 2010-02-28 20:21:20

1

這是一個標準的 - 和很好理解 - 「硬問題」,在SQL。

所有的弧節點圖理論問題都很難,因爲它們涉及傳遞關係。

有標準的解決方案。

  1. 使用顯式堆棧循環來管理樹的未訪問節點列表。

  2. 遞歸。這非常有效。它不會「需要棘手的緩存」它非常簡單而且非常有效。遞歸堆棧是未訪問節點的列表。

  3. 創建目錄樹的「transitive closure」。

  4. 用於處理傳遞關係(如目錄樹)的SQL擴展。