MAIN | |
LINE NUM | Type |
10 | |
20 | LINE |
30 | VALUE |
40 | VALUE |
50 | LINE |
60 | LINE |
70 | VALUE |
80 | VALUE |
90 | LINE |
100 | VALUE |
LINE | |||
LINE NUM | operator | LINE NUM From | LINE NUM To |
20 | + | 30 | 30 |
20 | - | 40 | 40 |
50 | + | 20 | 20 |
50 | + | 90 | 90 |
60 | + | 80 | 90 |
60 | + | 90 | 90 |
VALUE | ||
LINE NUM | sign | Amt |
30 | + | 5 |
40 | + | 20 |
70 | + | 30 |
80 | + | 40 |
100 | - | 10 |
Final | ||
LINE NUM | Amt | 연산 |
10 | 0 | |
20 | -15 | LINE 30 - LINE 40 |
30 | 5 | VALUE 30 |
40 | 20 | VALUE 40 |
50 | -25 | LINE 20 + LINE 90 |
60 | 20 | (LINE 80+LINE90) + LINE 90 |
70 | 30 | VALUE 70 |
80 | 40 | VALUE 80 |
90 | -10 | LINE 100 |
100 | -10 | VALUE 100 |
안녕하세요. 가진 지식이 부족해서 질문 남겨봅니다.
3개의 테이블 [ MAIN/LINE/VALUE ] 존재합니다.
1. MAIN 테이블은 라인정보와 어떤테이블을 바라봐야 하는지에 대한 유형정보가 있습니다.
2. LINE 테이블은 지정된 라인을 찾아가서 값을 가져와 연산해야되는 데이터가 있습니다.
(여기서 From To 는 같은 라인이면 한라인만 보여주고, 다른 라인이면 무조건 + 처리가 됩니다.)
3. VALUE 테이브은 각 라인의 값과 그 값에 대한 부호처리정보가 있습니다.
마지막 표를 보시면 연산 규칙에 따라 값이 나와야 합니다.
쿼리로는 방법이 생각이 안나서 템프 테이블에 담아서 처리하려고 하니
계산 순서가 뒤죽박죽이다보니 처리하는데 어려움이 있네요....
이전에도 도움 받았는데 염치불구하고 한번더 도움 부탁드립니다!!
WITH main AS ( SELECT 10 line_num, '' type FROM dual UNION ALL SELECT 20, 'LINE' FROM dual UNION ALL SELECT 30, 'VALUE' FROM dual UNION ALL SELECT 40, 'VALUE' FROM dual UNION ALL SELECT 50, 'LINE' FROM dual UNION ALL SELECT 60, 'LINE' FROM dual UNION ALL SELECT 70, 'VALUE' FROM dual UNION ALL SELECT 80, 'VALUE' FROM dual UNION ALL SELECT 90, 'LINE' FROM dual UNION ALL SELECT 100, 'VALUE' FROM dual ) , line AS ( SELECT 20 line_num, '+' operator, 30 line_num_from, 30 line_num_to FROM dual UNION ALL SELECT 20, '-', 40, 40 FROM dual UNION ALL SELECT 50, '+', 20, 20 FROM dual UNION ALL SELECT 50, '+', 90, 90 FROM dual UNION ALL SELECT 60, '+', 80, 90 FROM dual UNION ALL SELECT 60, '+', 90, 90 FROM dual UNION ALL SELECT 90, '+', 100, 100 FROM dual -- 요 라인이 누락된 듯? ) , value AS ( SELECT 30 line_num, '+' sign, 5 amt FROM dual UNION ALL SELECT 40, '+', 20 FROM dual UNION ALL SELECT 70, '+', 30 FROM dual UNION ALL SELECT 80, '+', 40 FROM dual UNION ALL SELECT 100, '-', 10 FROM dual ) , tmp(line_num, sign, amt, line_num_from, line_num_to) AS ( SELECT m.line_num , DECODE(l.operator, '-', -1, 1) * DECODE(v.sign, '-', -1, 1) sign , NVL(v.amt, 0) amt , l.line_num_from , l.line_num_to FROM main m LEFT OUTER JOIN line l ON m.type = 'LINE' AND m.line_num = l.line_num LEFT OUTER JOIN value v ON m.type = 'VALUE' AND m.line_num = v.line_num UNION ALL SELECT p.line_num , p.sign * DECODE(l.operator, '-', -1, 1) * DECODE(v.sign, '-', -1, 1) sign , v.amt , l.line_num_from , l.line_num_to FROM tmp p INNER JOIN main m ON m.line_num BETWEEN p.line_num_from AND p.line_num_to LEFT OUTER JOIN line l ON m.type = 'LINE' AND m.line_num = l.line_num LEFT OUTER JOIN value v ON m.type = 'VALUE' AND m.line_num = v.line_num ) SELECT line_num , SUM(sign * amt) amt FROM tmp GROUP BY line_num ORDER BY line_num ;
-- 10G 용 SELECT line_num , SUM(sign * amt) amt FROM (SELECT CONNECT_BY_ROOT(m.line_num) line_num , DECODE(MOD(REGEXP_COUNT( SYS_CONNECT_BY_PATH(l.operator, ',')||v.sign , '-'), 2), 1, -1, 1) sign , NVL(v.amt, 0) amt FROM main m LEFT OUTER JOIN line l ON m.type = 'LINE' AND m.line_num = l.line_num LEFT OUTER JOIN value v ON m.type = 'VALUE' AND m.line_num = v.line_num WHERE CONNECT_BY_ISLEAF = 1 CONNECT BY m.line_num BETWEEN PRIOR l.line_num_from AND PRIOR l.line_num_to ) GROUP BY line_num ORDER BY line_num ;
괄호는 존재하지 않고, 식의 순서를 의미하는 seq 필드가 있습니다.
기타 연산자(괄호는 실제 DB 값)로는 +(+), -(-), ×(*), ÷(/), MOD(%) 5가지가 있습니다.
LINE | ||||
LINE NUM | seq | operator | LINE NUM From | LINE NUM To |
20 | 10 | + | 30 | 30 |
20 | 20 | - | 40 | 40 |
50 | 10 | + | 20 | 20 |
50 | 20 | × | 90 | 90 |
60 | 10 | + | 80 | 90 |
60 | 20 | ÷ | 70 | 70 |
Result |
||
LINE NUM | Amt | 연산 |
10 | 0 | |
20 | -15 | LINE 30 - LINE 40 |
30 | 5 | VALUE 30 |
40 | 20 | VALUE 40 |
50 | 150 | LINE 20 × LINE 90 |
60 | 1 | (LINE 80+LINE90) ÷ LINE 70 |
70 | 30 | VALUE 70 |
80 | 40 | VALUE 80 |
90 | -10 | LINE 100 |
100 | -10 | VALUE 100 |
답변주신 쿼리에서 응용이 가능한지 문의 드립니다...
MASTER | ||
LINE NUM | SIGN_FLAG | Type |
10 | Y | |
20 | Y | Line |
30 | Value | |
40 | Value | |
50 | Y | Line |
60 | Y | Line |
70 | Y | Value |
80 | Y | Value |
90 | Y | Line |
100 | Y | Value |
Master 테이블에 SIGN_FLAG 란 필드가 추가되었습니다.
이 필드는 해당 라인을 연산해온 결과 값에 부호를 변경하라는 의미입니다.
위 테이블을 적용하고 계산을 하게 되면, 아래와 같은 결과로 출력되어야 합니다.
Final | ||
LINE NUM | Amt | 연산 |
10 | 0 | |
20 | 15 | ( LINE 30 - LINE 40 ) * -1 |
30 | 5 | VALUE 30 |
40 | 20 | VALUE 40 |
50 | -5 | (LINE 20 + LINE 90) * -1 |
60 | 60 | ((LINE 80+LINE90) + LINE 90) * -1 |
70 | -30 | VALUE 70 * -1 |
80 | -40 | VALUE 80 * -1 |
90 | -10 | LINE 100 * -1 |
100 | 10 | VALUE 100 * -1 |
-- 1. 재귀함수 생성 -- CREATE OR REPLACE FUNCTION f_rec_test(v_line_num NUMBER) RETURN NUMBER IS v_ret1 NUMBER := 0; v_ret2 NUMBER := 0; BEGIN BEGIN SELECT DECODE(sign, '-', -1, 1) * amt INTO v_ret1 FROM value WHERE line_num = v_line_num ; EXCEPTION WHEN NO_DATA_FOUND THEN FOR c1 IN (SELECT * FROM line l WHERE l.line_num = v_line_num ORDER BY seq ) LOOP v_ret2 := 0; FOR c2 IN (SELECT * FROM main WHERE line_num BETWEEN c1.line_num_from AND c1.line_num_to ) LOOP v_ret2 := v_ret2 + f_rec_test(c2.line_num); END LOOP; IF c1.operator = '+' THEN v_ret1 := v_ret1 + v_ret2; ELSIF c1.operator = '-' THEN v_ret1 := v_ret1 - v_ret2; ELSIF c1.operator = '×' THEN v_ret1 := v_ret1 * v_ret2; ELSIF c1.operator = '÷' THEN v_ret1 := v_ret1 / v_ret2; ELSIF c1.operator = '%' THEN v_ret1 := MOD(v_ret1, v_ret2); END IF; END LOOP; END; RETURN v_ret1; END; / -- 2. 재귀함수 호출 -- SELECT line_num , f_rec_test(line_num) x FROM main ;
-- 1. 재귀함수 생성 -- CREATE OR REPLACE FUNCTION f_rec_test(v_line_num NUMBER, v_sign_flag VARCHAR2) RETURN NUMBER IS v_ret1 NUMBER := 0; v_ret2 NUMBER := 0; BEGIN BEGIN SELECT DECODE(sign, '-', -1, 1) * amt INTO v_ret1 FROM value WHERE line_num = v_line_num ; EXCEPTION WHEN NO_DATA_FOUND THEN FOR c1 IN (SELECT * FROM line l WHERE l.line_num = v_line_num ORDER BY seq ) LOOP v_ret2 := 0; FOR c2 IN (SELECT * FROM main WHERE line_num BETWEEN c1.line_num_from AND c1.line_num_to ) LOOP v_ret2 := v_ret2 + f_rec_test(c2.line_num); END LOOP; IF c1.operator = '+' THEN v_ret1 := v_ret1 + v_ret2; ELSIF c1.operator = '-' THEN v_ret1 := v_ret1 - v_ret2; ELSIF c1.operator = '×' THEN v_ret1 := v_ret1 * v_ret2; ELSIF c1.operator = '÷' THEN v_ret1 := v_ret1 / v_ret2; ELSIF c1.operator = '%' THEN v_ret1 := MOD(v_ret1, v_ret2); END IF; END LOOP; END; IF v_sign_flag = 'Y' THEN v_ret1 := v_ret1 * -1; END IF; RETURN v_ret1; END; / -- 2. 재귀함수 호출 -- SELECT line_num , f_rec_test(line_num, sign_flag) x FROM main ;
안녕하세요. 마농님.
죄송하지만 위에 10g 용으로 구현하신 쿼리에 추가하는 것은 어려울까요?
추가로 REGEXP_COUNT 함수는 현재 환경에서 사용이 불가능하여 아래로 변경해봤는데
맞는지 확인 가능할까요?
-기존
DECODE(MOD(REGEXP_COUNT(SYS_CONNECT_BY_PATH(l.operator, ',')||v.sign, '-'), 2), 1, -1, 1) sign
-변경
DECODE(MOD(LENGTH(SYS_CONNECT_BY_PATH(l.operator,',')||v.sign) - LENGTH(REPLACE(SYS_CONNECT_BY_PATH(l.operator,',')||v.sign,'-')),2),1,-1,1) as sign