CTE递归查询两个具有多对多关系的表

时间:2015-06-30 14:20:01

标签: sql sql-server common-table-expression recursive-query

我有三个表,我需要一个查询来正确返回结果。

第一个表是包含父类别和子类别的类别表。该表允许所有级别的子表,包括子类别的子表。

第二个表是一个可以属于类别的文件表。这与类别表有很多关系。这些文件也可以属于包含子类别的类别。

第三个表创建了文件和类别之间的多对多关系。

第一个表格是Categories

CREATE TABLE Categories (
  category_id INT NOT NULL PRIMARY KEY,
  category_name varchar(20) NOT NULL,
  parent_id INT NOT NULL
);

第二个表格是Files

CREATE TABLE Files (
  file_id INT NOT NULL PRIMARY KEY,
  file_name varchar(20) NOT NULL
);

第三个表将文件链接到类别。文件可以属于任何类别,可以属于多个类别

CREATE TABLE Category_File (
  category_id INT NOT NULL,
  file_id INT NOT NULL
);

表格填写如下:

INSERT INTO Categories (category_id, category_name, parent_id) VALUES
(1, 'Cat1', 0),(2, 'Cat2', 0),(3, 'Cat3', 1),
(4, 'Cat4', 2),(5, 'Cat5', 1),(6, 'Cat6', 0),
(7, 'Cat7', 5),(8, 'Cat8', 4),(9, 'Cat9', 7);

INSERT INTO Files (file_id, file_name) VALUES
(1, 'File1'),(2, 'File2'),(3, 'File3'),
(4, 'File4'),(5, 'File5'),(6, 'File6');

INSERT INTO Category_File (category_id, file_id) VALUES
(3, 1),(4, 2),(5, 3),
(9, 6),(7, 2),(5, 4),
(8, 4),(6, 1),(3, 5);

我需要返回以显示按名称排序的类别,然后显示按名称排序的文件。因此,查询包含所有列的所有记录将导致

Result Set 1
[
category_id,    category_name,  file_id,    file_name   parent_id
1               Cat1            Null        Null        0
3               Cat3            Null        Null        1
3               Cat3            1           File1       Null
3               Cat3            5           File5       Null
5               Cat5            Null        Null        1   
5               Cat5            3           File3       Null    
5               Cat5            4           File4       Null
7               Cat7            Null        Null        5
7               Cat7            2           File2       Null
9               Cat9            Null        Null        7
9               Cat9            6           File6       Null
2               Cat2            Null        Null        0
4               Cat4            Null        Null        2
4               Cat4            2           File2       Null
8               Cat8            Null        Null        4
8               Cat8            4           File4       Null
6               Cat6            Null        Null        0
6               Cat6            1           File1       Null
]

2 个答案:

答案 0 :(得分:1)

declare @Categories table(
  category_id INT NOT NULL PRIMARY KEY,
  name varchar(20) NOT NULL,
  parent_id INT NOT NULL
);

declare @Files table(
  file_id INT NOT NULL PRIMARY KEY,
  name varchar(20) NOT NULL
);

declare @Category_File table(
  category_id INT NOT NULL,
  file_id INT NOT NULL
);

INSERT INTO @Categories (category_id, name, parent_id) VALUES
(1, 'Cat1',  0),
(2, 'Cat2', 0),
(3, 'Cat3', 1),
(4, 'Cat4', 2),
(5, 'Cat5', 1),
(6, 'Cat6', 0),
(7, 'Cat7', 5),
(8, 'Cat8', 4),
(9, 'Cat9', 7);


INSERT INTO @Files (file_id, name) VALUES
(1, 'File1'),
(2, 'File2'),
(3, 'File3'),
(4, 'File4'),
(5, 'File5'),
(6, 'File6');

INSERT INTO @Category_File (category_id, file_id) VALUES
(3, 1),
(4, 2),
(5, 3),
(9, 6),
(7, 2),
(5, 4),
(8, 4),
(6, 1),
(3, 5);


;with cteC as
(
    select c.category_id, c.name as category_name, cast(null as int) as file_id, cast(null as varchar(max)) as file_name, c.parent_id
    from @Categories c 
    where parent_id = 0 
    union all
    select category_id,name,file_id,file_name,parent_id
    from
    (
        select ch.category_id, ch.name, null file_id , null file_name, ch.parent_id
        from cteC c
            join @Categories ch on ch.parent_id = c.category_id

    )sq
)
select c.category_id, c.category_name, cast(null as int) as file_id, cast(null as varchar(max)) as file_name, c.parent_id
from cteC c  
union all       
select c.category_id, c.category_name, f.file_id, cast(f.name as varchar(max)), cast(null as int)
from cteC c
    join @Category_File cf on cf.category_id = c.category_id
        and c.file_id is null
    join @Files f on f.file_id = cf.file_id  
order by category_name, file_name

答案 1 :(得分:0)

此查询应该有效。它建立在@Oleg给出的答案之上,但有一些简化,并添加了一个级别属性以确保正确的排序。

我把它变成了一个社区维基,因为我不想为别人的工作赢得赞誉......

;with cte as
(
    select c.category_id, c.category_name, c.parent_id, cast(category_id as varchar(max)) as lvl
    from categories c where parent_id = 0 
    union all
    select ch.category_id, ch.category_name, ch.parent_id, c.lvl + ',' + cast(ch.category_id as varchar(max)) as lvl 
    from cte c join categories ch on ch.parent_id = c.category_id
)

select category_id, category_name, file_id, file_name, parent_id 
from (
    select c.category_id, c.category_name, null as file_id, null as file_name, c.parent_id, lvl
    from cte c  
    union all       
    select c.category_id, c.category_name, f.file_id, f.file_name, null, lvl
    from cte c
    join category_file cf on cf.category_id = c.category_id
    join files f on f.file_id = cf.file_id 
) a 
order by lvl, category_name, file_name