使用SQL Server检索引用树

时间:2016-02-03 13:35:00

标签: sql sql-server

我有一个管理文件的数据库 - 一些文件包含/引用其他文件,我的目标是设计一个查询,它可以为给定文档提供整个“树”。

例如,结构可能如下所示:

  • 文件1
    • 文件2
    • 文件3
    • 文件4
    • 文件5
      • 文件6
      • 文件7
      • 文件8
        • 文件9
        • 文件10

等,其中文件1有效地包含其后的所有文件

这些在我的数据库中在两个表之间分解 - 让我们称之为“文件”表和“引用”表

“文件”表包含有关文件本身的信息 - 文件ID,文件名等

“引用”表使用文件的FileID显示上述结构的关系。我的问题是,例如,文件1未引用文件6 - 它仅由文件5引用。

e.g:

[ParentFileID]  [ChildFileID]

1               2
1               3
1               4
1               5
5               6
5               7
5               8
8               9
8               10

理想情况下,我希望能够检查整个结构中我传入的任何给定FileID的位置

有什么想法吗?我一直在读CTE,感觉就像某种递归公用表表达式是我所追求的,尽管我能找到的每个例子都是使用一个表并涉及NULL来追踪顶级元素。

1 个答案:

答案 0 :(得分:1)

是的,可以使用递归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 |
+---------+

references描述了一个图表。由于主键,一个节点只能有一个父节点,但没有什么可以阻止循环。例如,请考虑以下数据:

+-------+--------+
| child | parent |
+-------+--------+
|     1 |      2 |
|     2 |      3 |
|     3 |      1 |
+-------+--------+

如您所见,此图表上有周期。