T-SQL - 带和的复杂递归

时间:2013-03-08 18:03:45

标签: tsql tree recursive-query

我为长期的问题描述道歉,但我无法将其分解为更多。 在继续阅读之前,请记住我的最终目标是T-SQL(可能是一些递归CTE?)。然而,在正确的方向上的推动将非常受欢迎(我已经尝试了一百万件事情并且已经抓了几个小时)。

考虑以下问题:我有一个类别表,通过ParentCategoryID-> CategoryID自我引用:

-----------------------
|       Category      |
-----------------------
|   *CategoryID       |
|   Name              |
|   ParentCategoryID  |
-----------------------

这种类型的表允许我构建一个树结构,比如说:

             -----------------  
             | parentCategory|
             -----------------
             /       |       \
           child(1) child   child
           /   \
      child(2) child(3)

其中“孩子”的意思是“孩子类别”(忽略这些数字,我稍后会解释)。显然,我可以在任何级别拥有尽可能多的孩子。

我每天编写的程序都会将值存储到“ValueRegistration”表中,该表连接到“Category”,如下所示:

------------------------        ----------------------          ----------------------
|   ValueRegistration  |        |       Item         |          |     Category       |
------------------------        ----------------------          ----------------------
|    *RegID            |        |   *ItemID          |          |   *CategoryID      |
|    Date              |>-------|   CategoryID       |>---------|   Name             |
|    ItemID            |        |   ItemTypeID       |          |   ParentCategoryID |
|    Value             |        ----------------------          ----------------------
------------------------                  Y
                                          |
                                          |
                                ---------------------
                                |   ItemType        |
                                ---------------------
                                |  *ItemTypeID      |
                                |   ItemType        |
                                ---------------------

如您所见,ValueRegistration涉及特定的项目,而该项目又属于某个类别。该类别可能有也可能没有父母(以及祖父母和曾祖父母等)。例如,它可能是上面所示的树中左下角(数字2)的孩子。此外,Item是某个ItemType。

我的目标:
我每天将值注册到ValueRegistration表(换句话说,Date和ItemID组合也是主键)。我希望能够在以下表单中检索结果集:

[ValueRegistration.Date, ItemType.ItemTypeID, Category.CategoryID, Value]

这看起来很简单(显然只是一堆连接)。但是,我还想要在ValueRegistration表中实际不存在的行的结果,即结果,其中给定日期和itemID的兄弟节点的值相加,并且生成新行 ValueRegistration.Date ItemType.ItemTypeID 与子节点中的相同,但 CategoryID 是子节点的父节点。请记住,结果集中此类行不存在Item。

例如,考虑一个场景,我在一堆日期和一堆ItemID上有子2和3的ValueRegistrations。显然,每个注册属于某个ItemType和Category。读者应该清楚

ValueRegistration.Date, ItemType.ItemTypeID, Category.CategoryID

是一个足以识别特定ValueRegistration的键(换句话说,它可以在不创建临时Item行的情况下解决我的问题),因此我可以内连接所有表,例如,以下结果:< / p>

ValueReg.Date, ItemType.ItemTypeID, Category.CategoryID, ValueReg.Value
08-mar-2013, 1, 5, 200
08-mar-2013, 1, 6, 250

现在假设我有四个类别行,如下所示:

1, category1, NULL
2, category2, 1
5, category5, 2
6, category6, 2

即。类别1是类别2的父类,类别2是类别5和类别6的父类别。类别1没有父类。我现在希望将以下行追加到结果集中:

08-mar-2013, 1, 2, (200+250)
08-mar-2013, 1, 1, (200+250+sum(values in all other childnodes of node 1)

记住:

  1. 解决方案需要递归,以便在树中向上执行(直到达到NULL)
  2. 对于计算的树节点,项目行不存在,因此必须使用CategoryID和ItemTypeID
  3. 是的,我知道我可以简单地创建“虚拟”项目行并在我最初插入我的数据库时添加ValueRegistrations,但这种解决方案容易出错,特别是如果其他程序员编码我的数据库但是忘记或不知道结果必须传递给父节点。相反,根据请求计算此解决方案的解决方案更安全,坦率地说,更优雅。

我试图按照this的方式设置一些内容,但我似乎不得不按日期和ItemTypeID进行分组,这在CTE中是不允许的。我的程序员只想创建一个递归函数,但我真的很难在SQL中做到这一点。

任何人都知道从哪里开始,我应该尝试什么,甚至(手指交叉)解决方案?

谢谢!

亚历山大

编辑: SQL FIDDLE

CREATE TABLE ItemType(
  ItemTypeID INT PRIMARY KEY,
  ItemType VARCHAR(50)
);


CREATE TABLE Category(
  CategoryID INT PRIMARY KEY,
  Name VARCHAR(50),
  ParentCategoryID INT,
  FOREIGN KEY(ParentCategoryID) REFERENCES Category(CategoryID)
  );

CREATE TABLE Item(
  ItemID INT PRIMARY KEY,
  CategoryID INT NOT NULL,
  ItemTypeID INT NOT NULL,
  FOREIGN KEY(CategoryID) REFERENCES Category(CategoryID),
  FOREIGN KEY(ItemTypeID) REFERENCES ItemType(ItemTypeID)
  );

CREATE TABLE ValueRegistration(
  RegID INT PRIMARY KEY,
  Date DATE NOT NULL,
  Value INT NOT NULL,
  ItemID INT NOT NULL,
  FOREIGN KEY(ItemID) REFERENCES Item(ItemID)
  );

INSERT INTO ItemType VALUES(1, 'ItemType1'); 
INSERT INTO ItemType VALUES(2, 'ItemType2');

INSERT INTO Category VALUES(1, 'Category1', NULL);   -- Top parent (1)
INSERT INTO Category VALUES(2, 'Category2', 1);      -- A child of 1
INSERT INTO Category VALUES(3, 'Category3', 1);      -- A child of 1
INSERT INTO Category VALUES(4, 'Category4', 2);      -- A child of 2
INSERT INTO Category VALUES(5, 'Category5', 2);      -- A child of 2
INSERT INTO Category VALUES(6, 'Category6', NULL);   -- Another top parent

INSERT INTO Item VALUES(1, 4, 1);    -- Category 4, ItemType 1
INSERT INTO Item VALUES(2, 5, 1);    -- Category 5, ItemType 1
INSERT INTO Item VALUES(3, 3, 1);    -- Category 3, ItemType 1
INSERT INTO Item VALUES(4, 1, 2);    -- Category 1, ItemType 2

INSERT INTO ValueRegistration VALUES(1, '2013-03-08', 100, 1);
INSERT INTO ValueRegistration VALUES(2, '2013-03-08', 200, 2);
INSERT INTO ValueRegistration VALUES(3, '2013-03-08', 300, 3);
INSERT INTO ValueRegistration VALUES(4, '2013-03-08', 400, 4);
INSERT INTO ValueRegistration VALUES(5, '2013-03-09', 120, 1);
INSERT INTO ValueRegistration VALUES(6, '2013-03-09', 220, 2);
INSERT INTO ValueRegistration VALUES(7, '2013-03-09', 320, 3);
INSERT INTO ValueRegistration VALUES(8, '2013-03-09', 420, 4);

-- -------------------- RESULTSET I WANT ----------------------
--  vr.Date    | ItemType    | CategoryTypeID  |  Value
-- ------------------------------------------------------------
--  2013-03-08 | 'ItemType1' | 'Category4'     | 100              Directly available
--  2013-03-08 | 'ItemType1' | 'Category5'     | 200              Directly available
--  2013-03-08 | 'ItemType1' | 'Category3'     | 300              Directly available
--  2013-03-08 | 'ItemType1' | 'Category2'     | 100+200          Calculated tree node
--  2013-03-08 | 'ItemType1' | 'Category1'     | 100+200+300      Calculated tree node
--  2013-03-08 | 'ItemType2' | 'Category1'     | 400              Directly available
--  2013-03-09 | 'ItemType1' | 'Category4'     | 120              Directly available
--  2013-03-09 | 'ItemType1' | 'Category5'     | 220              Directly available
--  2013-03-09 | 'ItemType1' | 'Category3'     | 320              Directly available
--  2013-03-09 | 'ItemType1' | 'Category2'     | 120+220          Calculated tree node
--  2013-03-09 | 'ItemType1' | 'Category1'     | 120+220+320      Calculated tree node
--  2013-03-09 | 'ItemType2' | 'Category1'     | 420              Directly available

1 个答案:

答案 0 :(得分:1)

如果将所有连接替换为具有此动态关系的连接的表类别,您将获得您正在寻找的层次结构:

with Category as (
  select * from ( values
    (1,'Fred',null),
    (2,'Joan',1),
    (3,'Greg',2),
    (4,'Jack',2),
    (5,'Jill',4),
    (6,'Bill',3),
    (7,'Sam',6)
  ) Category(CategoryID,Name,ParentCategoryID)
)
, Hierarchy as (
  select 0 as [Level],* from Category
--  where Parent is null

  union all

  select super.[Level]+1, sub.CategoryID, super.Name, super.ParentCategoryID
  from Category as sub
  join Hierarchy as super on super.CategoryID = sub.ParentCategoryID and sub.ParentCategoryID is not null
) 
select * from Hierarchy
-- where CategoryID = 6
-- order by [Level], CategoryID

例如,取消注释底部的两行将产生此结果集:

Level       CategoryID  Name ParentCategoryID
----------- ----------- ---- ----------------
0           6           Bill 3
1           6           Greg 2
2           6           Joan 1
3           6           Fred NULL