I tried to write the query without using common-table-
expression(including recursive cte).
Here is the example.
Apart from its lengthy and complex code,
the most problem(I thought) was repeating series of nested-table-
expressions, like...
g1 ---> g ---> g2 g ---> g3 ---> g --->g4 ---> g
(g0 is necessary)
I used 4 level of nesting in the example,
but the nesting level must be incresed depending on the number of
rows in the table.
Note: A common-table-expression T was used to make test data.
As you know, it can be replaced by a CREATE TABLE statement and an
INSERT statement.
Example 1:
------------------------- Commands Entered
------------------------------
WITH
T (prio, status, credit, limit) AS (
VALUES
( 1 , 0, 10 , 15 ) -- 10 1 - 1
, ( 2 , 1, 2 , 15 ) --
, ( 3 , 0, 10 , 15 ) -- 10
, ( 4 , 0, 1 , 15 ) -- 11 3 - 4
, ( 5 , 1, 10 , 15 ) --
, ( 6 , 0, 8 , 15 ) -- 5
, ( 7 , 0, 5 , 15 ) -- 13
, ( 8 , 0, 2 , 15 ) -- 15 6 - 8
, ( 9 , 3, 10 , 15 ) --
, ( 10 , 0, 2 , 15 ) -- 2
, ( 11 , 0, 3 , 15 ) -- 5
, ( 12 , 0, 4 , 15 ) -- 9
, ( 13 , 0, 5 , 15 ) -- 14 10 - 13
, ( 14 , 0, 6 , 15 ) -- 6 14 - 14
, ( 15 , 0, 10 , 15 ) -- 19 15 - 15
, ( 16 , 2, 7 , 15 ) --
)
SELECT t.*
, SUM( CASE t.status WHEN 0 THEN t.credit END )
OVER( PARTITION BY low_prio
ORDER BY t.prio ) AS acc2
FROM t
LEFT OUTER JOIN
(SELECT low_prio
, LEAD( low_prio , 1 , 0 )
OVER(ORDER BY low_prio) - 1 AS high_prio
/* level 4 */
FROM (
SELECT low_prio , high_prio
, low_rnum , high_rnum
FROM (-- g3
SELECT g.*
, '*'
Quote:
| LISTAGG( CHAR(high_rnum) || '*' )
OVER(ORDER BY low_rnum) AS
|
high_rnum_list
/* level 3 */
FROM (
SELECT low_prio , high_prio
, low_rnum , high_rnum
FROM (-- g3
SELECT g.*
, '*'
Quote:
| LISTAGG( CHAR(high_rnum) || '*' )
OVER(ORDER BY low_rnum) AS
|
high_rnum_list
/* level 2 */
FROM (
SELECT low_prio , high_prio
, low_rnum , high_rnum
FROM (-- g2
SELECT g.*
, '*' || LISTAGG( CHAR(high_rnum) ||
'*' )
OVER(ORDER BY low_rnum)
high_rnum_list
/* level 1 */
FROM (
SELECT low_prio , high_prio
, low_rnum , high_rnum
FROM (-- g1
SELECT g.*
, '*' || LISTAGG( CHAR(high_rnum) ||
'*' )
OVER(ORDER BY low_rnum)
high_rnum_list
/* level 0 */
FROM (
SELECT low.prio AS
low_prio
, MAX( NULLIF(acc.prio , high.prio) ) AS
high_prio
, MIN(low.rnum) AS
low_rnum
, MAX( high.rnum ) - 1 AS
high_rnum
FROM (SELECT t.*
, INT( ROW_NUMBER() OVER(ORDER BY prio) )
AS rnum
FROM T
WHERE status = 0
) AS low
INNER JOIN
(SELECT COALESCE( prio
, (SELECT MAX(prio) + 1
FROM T)
) AS
prio
, MAX(limit) AS
limit
, INT( ROW_NUMBER()
OVER(ORDER BY prio) ) AS
rnum
FROM T
WHERE status = 0
GROUP BY
ROLLUP ( (prio) )
) AS high
ON high.prio >= low.prio
INNER JOIN
(SELECT COALESCE( prio
, (SELECT MAX(prio) + 1
FROM T)
) AS
prio
, COALESCE( credit , MAX(limit) + 1 ) AS
credit
FROM T
WHERE status = 0
GROUP BY
ROLLUP ( (prio , credit) )
) AS acc
ON acc.prio BETWEEN low.prio AND high.prio
GROUP BY
low.prio , high.prio , high.limit
HAVING
SUM( acc.credit ) > high.limit
AND SUM( acc.credit
* SIGN(high.prio - acc.prio)
) <= high.limit
) g
/* End of level 0 */
/* level 1 */
) g1
WHERE low_rnum = 1
OR LOCATE( '*' || CHAR(low_rnum - 1) || '*' ,
high_rnum_list ) > 0
) g
/* End of level 1 */
/* level 2 */
) g2
WHERE low_rnum = 1
OR LOCATE( '*' || CHAR(low_rnum - 1) || '*' ,
high_rnum_list ) > 0
) g
/* End of level 2 */
/* level 3 */
) g3
WHERE low_rnum = 1
OR LOCATE( '*' || CHAR(low_rnum - 1) || '*' ,
high_rnum_list ) > 0
) g
/* End of level 3 */
/* level 4 */
) g4
WHERE low_rnum = 1
OR LOCATE( '*' || CHAR(low_rnum - 1) || '*' ,
high_rnum_list ) > 0
) g
/* End of level 4 */
) g
ON t.prio BETWEEN g.low_prio AND g.high_prio
ORDER BY
prio
;
------------------------------------------------------------------------------
PRIO STATUS CREDIT LIMIT ACC2
----------- ----------- ----------- ----------- -----------
1 0 10 15 10
2 1 2 15 10
3 0 10 15 10
4 0 1 15 11
5 1 10 15 11
6 0 8 15 8
7 0 5 15 13
8 0 2 15 15
9 3 10 15 15
10 0 2 15 2
11 0 3 15 5
12 0 4 15 9
13 0 5 15 14
14 0 6 15 6
15 0 10 15 10
16 2 7 15 10
16 record(s) selected.