【问题标题】:T-SQL - Complicated recursion with sumT-SQL - sum 复杂的递归
【发布时间】:2013-03-08 18:03:44
【问题描述】:

对于冗长的问题描述,我深表歉意,但我无法将其分解为更多。 在继续阅读之前,请记住我的最终目标是 T-SQL(可能是一些递归 CTE?)。但是,非常感谢您朝正确的方向推进(我已经尝试了一百万种方法,并且已经摸不着头脑了好几个小时)。

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

---------------------- |类别 | ---------------------- | *类别ID | |姓名 | |父类别 ID | ----------------------

这种类型的表可以让我构建一个树形结构,比如:

----------------- |父类| ----------------- / | \ 小孩(1) 小孩小孩 / \ 儿童(2) 儿童(3)

其中“child”表示“子类别”(忽略数字,我稍后会解释)。显然,我可以在任何级别上拥有任意数量的孩子。

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

------------------------ ---------- ---- ------------------ |价值注册 | |项目 | |类别 | ------------------------ ---------- ---- ------------------ | *RegID | | *项目ID | | *类别ID | |日期 |>-------|类别ID |>---------|姓名 | |物品编号 | |项目类型ID | |父类别 ID | |价值 | ---------------------- ---------- ------------------------ 是 | | --------------------- |项目类型 | --------------------- | *ItemTypeID | |项目类型 | ---------------------

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

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

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

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

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

ValueRegistration.Date, ItemType.ItemTypeID, Category.CategoryID

是一个足以识别特定 ValueRegistration 的键(换句话说,无需创建临时 Item 行就可以解决我的问题),因此我可以内部连接所有表,例如,以下结果:

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. 计算的树节点将不存在 Item 行,因此必须使用 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

【问题讨论】:

  • 你能用表结构和示例数据设置一个 SQL Fiddle 吗?
  • @DominicGoulet 已添加!

标签: tsql tree recursive-query


【解决方案1】:

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

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

【讨论】:

  • 感谢您回复彼得!我已经摆弄了你的建议一段时间了。我正在努力了解如何在行中实现子节点的 SUM(Value)。我看到您使用 super.Value + 1 (这对于确定层次结构很好),但是您如何将这个想法概括为子节点的总和?我很难找到解决 SUM 和 GROUP BY 的方法,但这在 CTE 中是不合法的。我在上面的帖子中添加了 SQLFiddle 以提供帮助。再次感谢!亚历克斯
猜你喜欢
  • 2013-07-16
  • 1970-01-01
  • 1970-01-01
  • 2021-05-05
  • 2011-11-09
  • 1970-01-01
  • 2020-02-14
  • 2012-07-09
  • 1970-01-01
相关资源
最近更新 更多