【问题标题】:CTE recursive query for two tables that has a many to many relationship具有多对多关系的两个表的 CTE 递归查询
【发布时间】:2015-06-30 14:20:01
【问题描述】:

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

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

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

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

第一个表是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
]

【问题讨论】:

  • 您遇到错误了吗?你是如何解决这个问题的?
  • 很遗憾,我的sql知识不够强,无法写出这个查询。我玩过这个网站上的递归查询,它查询一个表,但是通过第二个表和 m-m 关系超过了我的水平。
  • @PostalJoe 包含用于创建和填充表的代码很好,但请确保代码没有任何错误...我已更改列名以便它们匹配插入脚本,还添加了一些缺少的逗号。
  • 抱歉,我没听清楚。你是对的。我在最后一分钟添加了 FK 约束,认为问题中需要它,但它可能应该被删除。

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


【解决方案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

【讨论】:

  • 感谢您的帮助。我真的很感激;但是,这些行没有以正确的顺序返回。例如,第 3 行 (Cat3) 应该在第 2 行 (Cat2) 之前返回,因为它是 Cat1 的子代。
  • 基本正确,除了顺序(还有一些不必要的部分)。
【解决方案2】:

这个查询应该有效。它建立在@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

【讨论】:

    猜你喜欢
    • 2022-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 2019-02-17
    • 2016-07-22
    • 2019-09-12
    相关资源
    最近更新 更多