如何添加门票(包括所有儿童门票)的总小时数?

问题描述 投票:0回答:1

我的一般问题是我有一个票证系统,其中一张票证可以有任意数量的子票证,并且每个孩子又可以有任何深度的子票证。我见过有的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

目标如下所示。凭票,直达时间和总时间。

如果不使用上面的数据,极度简化的情况会是这样的

sql sql-server recursion
1个回答
0
投票

根据您的数据编写代码,我想出了这个:

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 票。

© www.soinside.com 2019 - 2024. All rights reserved.