父子关系的SQL查询递归

时间:2012-02-09 15:02:58

标签: sql sql-server

如何使用CTE Recursion查询文件表中包含文件的文件夹?

[Folder_Table]

folder_id |parent_id |folder_name
1         |0         |Folder1
2         |1         |Folder2
3         |1         |Folder3
4         |2         |Folder4
5         |2         |Folder5
6         |3         |Folder6
7         |6         |Folder7
8         |0         |Folder8
9         |8         |Folder9
10        |8         |Folder10

[File_Table]

file_id   |folder_id |file_name
1         |4         |File1
2         |4         |File2
3         |5         |File3
4         |5         |File4
5         |9         |File5
6         |10        |File6

_______________________________________
Result (for all folders)

[+] Folder1
    [+] Folder2
        [+] Folder4
              File1
              File2
        [+] Folder5
              File3
              File4
    [+] Folder3
        [+] Folder6
            [+] Folder7
[+] Folder8
    [+] Folder9
          File5
    [+] Filder10
          File6
_______________________________________

我只想从文件夹表中检索链末尾有文件的行。所以在这种情况下,查询应该给我:

folder_id |parent_id |folder_name
1         |0         |Folder1
2         |1         |Folder2
4         |2         |Folder4
5         |2         |Folder5
8         |0         |Folder8
9         |8         |Folder9
10        |8         |Folder10

由于Folder7不包含任何文件,因此我不希望在结果集中返回Folder7,Folder6或Folder3。

2 个答案:

答案 0 :(得分:7)

这可能不是最优雅的解决方案:

WITH cte(folder_id, parent_id, name)
AS
(
    select [folder].folder_id, parent_id, name 
    from [folder] 
        join [file] on [folder].[folder_id] = [file].[folder_id]
    union all
    select [folder].[folder_id], [folder].parent_id, [folder].name 
    from cte 
        join [folder] on cte.parent_id = folder.folder_id

)
SELECT distinct * FROM cte

答案 1 :(得分:5)

略有不同的方式,包括最后的所有列

;WITH q AS (
  SELECT  ft.folder_id
  FROM    File_Table ft
          INNER JOIN Folder_Table f ON f.folder_id = ft.folder_id
  UNION ALL
  SELECT  f.parent_id
  FROM    Folder_Table f
          INNER JOIN q ON q.folder_id = f.folder_id
)
SELECT  DISTINCT f.folder_id
        , f.parent_id
        , f.folder_name
        , f.is_active
FROM    q
        INNER JOIN Folder_Table f ON f.folder_id = q.folder_id
WHERE   f.is_active = 1        

测试脚本

;WITH Folder_Table (folder_id, parent_id, folder_name, is_active) AS (
  SELECT * FROM (VALUES 
    (1, 0, 'Folder1', 1)
    , (2, 1, 'Folder2', 0)
    , (3, 1, 'Folder3', 1)
    , (4, 2, 'Folder4', 0)
    , (5, 2, 'Folder5', 0)
    , (6, 3, 'Folder6', 1)
    , (7, 6, 'Folder7', 1)
    , (8, 0, 'Folder8', 1)
    , (9, 8, 'Folder9', 1)
    , (10, 8, 'Folder10', 1)
  ) a (b, c, d, e)
)
, File_Table (filed_id, folder_id, file_name) AS (
  SELECT * FROM (VALUES 
    (1, 4, 'File1')
    , (2, 4, 'File2')
    , (3, 5, 'File3')
    , (4, 5, 'File4')
    , (5, 9, 'File5')
    , (6, 10, 'File6')
  ) a (b, c, d)
)
, q AS (
  SELECT  ft.folder_id
  FROM    File_Table ft
          INNER JOIN Folder_Table f ON f.folder_id = ft.folder_id
  UNION ALL
  SELECT  f.parent_id
  FROM    Folder_Table f
          INNER JOIN q ON q.folder_id = f.folder_id
)
SELECT  DISTINCT f.folder_id
        , f.parent_id
        , f.folder_name
        , f.is_active
FROM    q
        INNER JOIN Folder_Table f ON f.folder_id = q.folder_id
WHERE   f.is_active = 1