递归子目录SQL问题

时间:2010-02-28 20:07:42

标签: sql model recursion subdirectory

这是一段困扰我一段时间的心理练习。你会用什么策略来解决这类问题?

让我们考虑以下简单的数据库结构。我们有目录,显然是它们的一棵树。我们还有内容项,它们总是存在于某些目录中。

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能够很好地优化查询的事实。

你们有什么想法?

2 个答案:

答案 0 :(得分: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

Oracle之前的11g

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,请参阅此文:

答案 1 :(得分:1)

这是一个标准 - 并且很好理解 - SQL中的“难题”。

所有弧节点图论都很难,因为它们涉及传递关系。

有标准的解决方案。

  1. 使用显式堆栈进行循环,以管理树的未访问节点列表。

  2. 递归。这非常有效。它不“需要棘手的缓存”它非常简单而且非常有效。递归堆栈是未访问节点的列表。

  3. 创建目录树的“transitive closure”。

  4. 用于处理目录树等传递关系的SQL扩展。