我的一般问题是我有一个票证系统,其中一张票证可以有任意数量的子票证,并且每个孩子又可以有任何深度的子票证。我见过有的7层深,有的1层深。没有要求票证有父级,毕竟它可能是其他票证的顶层,或者可能没有子级或父级。
我正在尝试计算门票的工作时间,包括所有儿童门票+实际门票。例如,票证 30 没有直接的时间,但所有票证都起作用,因此它应该具有所有时间。类似的其他层有儿童门票和时间,所以他们应该显示,也许他们有 8 个直接小时,他们的孩子有 sum(16),所以 8 个直接小时 + sum(16) = 24 总小时。
我的第一个想法是“递归 cte 简单”,但后来意识到你不能在递归中执行外连接和聚合,而你需要两者。我的下一个想法是某种类型的具有设定周期的循环,例如执行 20 次,只是希望深度永远不会超过 20。然而,这是针对视图的,我实际上不知道深度。
drop table if exists tickets
CREATE TABLE tickets (
ticketid INT,
parentID INT
);
drop table if exists Hours
CREATE TABLE Hours (
ticketid INT,
hours INT
)
insert into tickets (ticketid , parentID )
values
('1','6')
,('2','7')
,('3','8')
,('4','9')
,('5','10')
,('6','18')
,('7','19')
,('8','20')
,('9','21')
,('10','22')
,('11','23')
,('12','18')
,('13','19')
,('14','20')
,('15','21')
,('16','22')
,('17','23')
,('18','24')
,('19','25')
,('20','26')
,('21','27')
,('22','28')
,('23','29')
,('24','30')
,('25','30')
,('26','30')
,('27','30')
,('28','30')
,('29','30')
,('30','')
insert into hours (ticketid, hours)
values
('22','4')
,('28','1')
,('15','1')
,('23','2')
,('16','1')
,('7','4')
,('12','2')
,('28','3')
,('19','4')
,('14','1')
,('10','4')
,('18','5')
,('3','2')
,('6','3')
,('7','2')
,('23','5')
,('26','2')
,('26','4')
,('14','2')
,('6','1')
,('24','4')
,('4','1')
,('4','5')
,('25','2')
,('0','4')
,('8','4')
,('20','2')
,('19','1')
,('6','1')
,('24','1')
,('26','2')
,('15','5')
,('1','1')
,('2','5')
,('3','2')
,('4','1')
,('5','2')
,('6','3')
,('7','2')
,('8','5')
,('9','2')
,('10','5')
,('11','1')
,('12','3')
,('13','2')
,('14','5')
,('15','3')
,('16','2')
,('17','4')
,('18','1')
,('19','3')
,('20','2')
,('21','5')
,('22','3')
,('23','4')
,('24','4')
,('25','3')
,('26','2')
,('27','3')
,('28','5')
,('29','4')
,('30','0')
,('1','2')
,('2','1')
,('3','5')
,('4','4')
,('5','3')
,('6','2')
,('7','1')
,('8','2')
,('9','5')
,('10','4')
,('11','1')
,('12','5')
,('13','2')
,('14','1')
,('15','5')
,('16','5')
,('17','4')
,('18','1')
,('19','1')
,('20','5')
,('21','3')
,('22','5')
,('23','3')
,('24','4')
,('25','4')
,('26','2')
,('27','2')
,('28','1')
,('29','1')
,('30','0')
;
with tot_hours as (
select t.ticketid,
t.parentid,
sum(hours) tot_hours
from tickets t
left join hours h
on t.ticketid = h.ticketid
group by t.ticketid
UNION ALL
select t.parentid,
ti.parentid,
sum(tot_hours) tot_hours
from tot_hours t
left join tickets ti
on t.parentid = ti.ticketid
join hours h
on t.ticketid = h.ticketid
group by t.parentid
)
select tot.* ,sum(h.hours)
from tot_hours tot
left join hours h
on tot.ticketid = h.ticketid
group by tot.ticketid
,tot.parentid
,tot.tot_hours
根据您的数据编写代码,我想出了这个:
WITH CTE_TREE AS (
SELECT parentid AS parentid, ticketid AS children
FROM tickets t
WHERE parentID <> 0
UNION
SELECT parentid, NULL
FROM tickets
WHERE parentID <> 0
UNION
SELECT ticketid, NULL
FROM tickets
)
, CTE_TRAVERSE AS (
SELECT parentid AS mainId, children AS nextParent
FROM CTE_TREE
UNION ALL
SELECT t.mainId, tree.children
FROM CTE_TREE tree
INNER JOIN CTE_TRAVERSE t
ON t.nextParent = tree.parentid
WHERE tree.children <> ''
)
SELECT t.MainID
, SUM(CASE WHEN t.nextparent IS NULL THEN h.Hours END) AS Direct_hours
, SUM(h.Hours) AS Total_hours
FROM CTE_TRAVERSE t
INNER JOIN Hours h
ON h.ticketid = t.nextparent
OR (h.ticketid = t.mainID AND t.nextparent IS NULL)
GROUP BY t.mainId
代码有点忙,但主要思想是反转结构,使其看起来像:parent_id => Children,而不是 Children => Parent,因为您对顶级信息感兴趣。
CTE_TREE 包含票证的所有组合,而 CTE_TRAVERSE 递归地从父级到子级,同时保留“顶部”父级 ID 作为主 ID。保留每张票的“主”行(没有子项)非常重要,这样以后就可以轻松加入“小时”表。
最后,我们根据子行(下一个父行)或没有子行的主行上的小时进行连接。
输出:
主ID | 直达_小时 | 总共_小时 |
---|---|---|
1 | 3 | 3 |
2 | 6 | 6 |
3 | 9 | 9 |
4 | 11 | 11 |
5 | 5 | 5 |
6 | 10 | 13 |
7 | 9 | 15 |
8 | 11 | 20 |
9 | 7 | 18 |
10 | 13 | 18 |
11 | 2 | 2 |
12 | 10 | 10 |
13 | 4 | 4 |
14 | 9 | 9 |
15 | 14 | 14 |
16 | 8 | 8 |
17 | 8 | 8 |
18 | 7 | 30 |
19 | 9 | 28 |
20 | 9 | 38 |
21 | 8 | 40 |
22 | 12 | 38 |
23 | 14 | 24 |
24 | 13 | 43 |
25 | 9 | 37 |
26 | 12 | 50 |
27 | 5 | 45 |
28 | 10 | 48 |
29 | 5 | 29 |
30 | 0 | 252 |
顺便说一句,我认为你对 29 的计算是错误的,因为它应该是 11,17,23,29 的总和,看来你错过了添加 11 票。