2

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

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

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

你们有什么感想?

4

2 回答 2

4

SQL Server 2005,PostgreSQL 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,请参阅这篇文章:

于 2010-02-28T20:12:27.543 回答
1

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

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

有标准的解决方案。

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

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

  3. 创建目录树的“传递闭包”。

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

于 2010-02-28T20:14:47.940 回答