这是一段困扰我一段时间的心理练习。你会用什么策略来解决这类问题?
让我们考虑以下简单的数据库结构。我们有目录,显然是它们的一棵树。我们还有内容项,它们总是存在于某些目录中。
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 :(得分:4)
在SQL Server 2005
,PostgreSQL 8.4
和Oracle 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中的“难题”。
所有弧节点图论都很难,因为它们涉及传递关系。
有标准的解决方案。
使用显式堆栈进行循环,以管理树的未访问节点列表。
递归。这非常有效。它不“需要棘手的缓存”它非常简单而且非常有效。递归堆栈是未访问节点的列表。
创建目录树的“transitive closure”。
用于处理目录树等传递关系的SQL扩展。