【问题标题】:Recursive subdirectory SQL problem递归子目录SQL问题
【发布时间】:2011-01-22 02:06:40
【问题描述】:

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

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

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 不能用于轻松获取子选择的所有 directoryId。我对么?

可以通过简单的递归循环在应用程序端解决这个问题。不过这实际上可能会变得非常繁重,并且需要复杂的缓存,尤其是要保证合理的首次访问时间。

也许还可以构建一个物化查询表并为其动态添加多维索引。可能但实施混乱。太复杂了。

我最喜欢的解决方案可能是添加一个新表,例如

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

并确保在移动/删除/创建目录时始终手动更新它。因此,我总是可以使用 directoryId 进行选择并获取子目录的所有 Id,包括作为更复杂查询的子选择。我也喜欢 rdbms 能够很好地优化查询这一事实。

你们觉得呢?

【问题讨论】:

  • 为什么是最后一票?这是一个完全有效的问题。

标签: sql model recursion subdirectory


【解决方案1】:

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,请看这篇文章:

【讨论】:

    【解决方案2】:

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

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

    有标准的解决方案。

    1. 带有显式堆栈的循环用于管理树中未访问节点的列表。

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

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

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

    【讨论】:

      猜你喜欢
      • 2020-01-10
      • 1970-01-01
      • 2017-03-26
      • 1970-01-01
      • 2011-01-10
      • 2011-06-19
      • 2013-06-07
      • 1970-01-01
      • 2016-10-23
      相关资源
      最近更新 更多