对于冗长的问题描述,我深表歉意,但我无法将其分解为更多。在继续阅读之前,请记住我的最终目标是 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.Date和ItemType 处生成新行。 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)
记住:
- 解决方案需要递归,以便在树中向上执行(直到达到 NULL)
- 计算的树节点将不存在 Item 行,因此必须使用 CategoryID 和 ItemTypeID
- 是的,我知道当我最初插入数据库时,我可以简单地创建“虚拟”项目行并添加 ValueRegistrations,但这种解决方案很容易出错,特别是如果其他程序员针对我的数据库编写代码但忘记或不知道结果必须传递给父节点。相反,根据请求计算此值的解决方案更安全,坦率地说,更优雅。
我试图按照this的方式设置一些东西,但我似乎不得不按 Date 和 ItemTypeID 分组,而这在 CTE 中是不允许的。我的程序员只想做一个递归函数,但我真的很难在 SQL 中做到这一点。
任何人都知道从哪里开始,我应该尝试什么,甚至(手指交叉)解决方案?
谢谢!
亚历山大
编辑:SQL 小提琴
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