从 SQL 到 SPL:递归关系下的合计

某数据库有票据表和工时表,票据表存储了每张票据和父票据的关系,形成了自关联结构:

ticketid

parentID

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

0

工时表存储了每张票据对应的多个工时:

ticketid

hours

0

4

1

2

1

1

2

5

2

1

3

5

3

2

3

2

4

1

4

4

4

1

4

5

5

3

5

2

6

3

6

3

6

2

6

1

6

1

7

1

7

2

7

4

7

2

8

5

8

2

8

4

9

5

9

2

10

5

10

4

10

4

11

1

11

1

12

3

12

2

12

5

13

2

13

2

14

5

14

1

14

2

14

1

15

5

15

5

15

1

15

3

16

2

16

1

16

5

17

4

17

4

18

1

18

5

18

1

19

1

19

1

19

4

19

3

20

2

20

2

20

5

21

3

21

5

22

3

22

4

22

5

23

3

23

2

23

5

23

4

24

4

24

4

24

1

24

4

25

2

25

4

25

3

26

2

26

2

26

4

26

2

26

2

27

2

27

3

28

5

28

1

28

3

28

1

29

1

29

4

30

0

30

0

现在要计算出每张票据的工时,并递归地计算出该票据和所有下级子票据的工时之和,即总工时。

MainID

Direct_hours

Total_hours

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

SQL:

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

SQL 要用多个子查询实现自关联和递归关系,代码复杂难懂。SPL 直接提供了引用函数可建立自关联,提供了递归函数可取所有下级节点:https://try.esproc.com/splx?2SJ


 A

1

$select t.ticketid ticketid,t.parentID parentID,h.hours hours from tickets.txt t left join (select ticketid,sum(hours) hours from hours.txt group by ticketid) h on t.ticketid=h.ticketid

2

>A1.switch(parentID,A1:ticketid)

3

=A1.new(ticketid:MainID,hours:Direct_hours,hours+A1.nodes(parentID,~).sum(hours):Total_hours)

A1:加载数据,在票据表的基础上,计算出每张票据的直接工时。

A2:让每条记录的父票据字段指向父票据记录,建立自关联关系。函数 switch 可以将字段值修改成记录。

A3:新建二维表,当前票据的总工时 = 当前票据的直接工时 + 当前票据所有的下级子票据的工时之和。函数 nodes 可以递归地计算出某条记录所有的下级记录。

问题来源:https://stackoverflow.com/questions/78089375/how-to-add-total-hours-for-a-ticket-including-all-child-tickets