我有一个管理文件的数据库 – 一些文件包含/引用其他文件,我的目标是设计一个查询,它可以为给定文档提供整个“树”.
例如,结构可能如下所示:
>文件1
>文件2
>文件3
>文件4
>文件5
>文件6
>文件7
>文件8
>文件9
>文件10
等等,其中文件1有效地包含其后的所有文件
这些在我的数据库中在两个表之间分解 – 让我们称之为“文件”表和“引用”表
“文件”表包含有关文件本身的信息 – 文件ID,文件名等.
“引用”表使用文件的FileID显示上述结构的关系.我的问题是,例如,文件1没有引用文件6 – 它仅由文件5引用.
例如.:
[ParentFileID] [ChildFileID]
1 2
1 3
1 4
1 5
5 6
5 7
5 8
8 9
8 10
理想情况下,我希望能够检查整个结构中我传入的任何给定FileID的位置
有任何想法吗?我一直在阅读有关CTE的信息,感觉就像某种递归公用表表达式是我所追求的,尽管我能找到的每个例子都使用一个表并涉及NULL来追踪顶级元素.
最佳答案 是的,可以使用递归CTE完成.
USE tempdb
GO
CREATE TABLE files
(
[file_id] int PRIMARY KEY,
[file_name] varchar(128) NOT NULL
);
INSERT INTO files VALUES
(1, 'File 1'),
(2, 'File 2'),
(3, 'File 3'),
(4, 'File 4'),
(5, 'File 5'),
(6, 'File 6'),
(7, 'File 7'),
(8, 'File 8'),
(9, 'File 9'),
(10, 'File 10');
CREATE TABLE [references]
(
parent_file_id int NOT NULL,
child_file_id int NOT NULL,
PRIMARY KEY (child_file_id)
);
INSERT INTO [references] VALUES
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(5, 6),
(5, 7),
(5, 8),
(8, 9),
(8, 10);
GO
CREATE FUNCTION dbo.get_file_with_path(@file_id int)
RETURNS TABLE
AS
RETURN WITH h
AS
(
SELECT
f.file_id, f.file_id as child_file_id,
f.file_name, 0 as reverse_level,
CAST( '/' + f.file_name as varchar(8000)) as path
FROM
dbo.files f
WHERE
f.file_id = @file_id
UNION ALL
SELECT
h.file_id, r.parent_file_id as child_file_id,
h.file_name, h.reverse_level + 1 as reverse_level,
CAST('/' + f.file_name + h.path as varchar(8000)) as path
FROM
h
INNER JOIN [references] r
ON h.child_file_id = r.child_file_id
INNER JOIN dbo.files f
ON f.file_id = r.parent_file_id
)
SELECT TOP(1) h.file_id, h.file_name, h.path
FROM h
ORDER BY h.reverse_level DESC;
GO
SELECT *
FROM dbo.get_file_with_path(1)
UNION ALL
SELECT *
FROM dbo.get_file_with_path(3)
UNION ALL
SELECT *
FROM dbo.get_file_with_path(6)
UNION ALL
SELECT *
FROM dbo.get_file_with_path(10)
输出:
| file_id | file_name | path |
|---------|-----------|-------------------------------|
| 1 | File 1 | /File 1 |
| 3 | File 3 | /File 1/File 3 |
| 6 | File 6 | /File 1/File 5/File 6 |
| 10 | File 10 | /File 1/File 5/File 8/File 10 |
当你说位置时,我假设你的意思是路径
编辑:
回答注释中的问题,您还可以创建一个表值函数,该函数返回给定节点下的子树:
CREATE FUNCTION dbo.get_file_subtree_excluding_self(@file_id int)
RETURNS TABLE
AS RETURN
WITH h AS
(
SELECT r.parent_file_id, r.child_file_id
FROM [references] r
WHERE r.parent_file_id = @file_id
UNION ALL
SELECT r.parent_file_id, r.child_file_id
FROM
h INNER JOIN [references] r
ON h.child_file_id = r.parent_file_id
)
SELECT h.child_file_id as [file_id]
FROM h
GO
SELECT * FROM dbo.get_file_subtree_excluding_self(5)
输出:
+---------+
| file_id |
+---------+
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
+---------+
表格引用描述了一个图表.由于主键,一个节点只能有一个父节点,但没有什么可以阻止循环.例如,请考虑以下数据:
+-------+--------+
| child | parent |
+-------+--------+
| 1 | 2 |
| 2 | 3 |
| 3 | 1 |
+-------+--------+
如您所见,此图表上有循环.