[퀴즈] 조건부 누적 쿼리 1 17 8,301

by 마농 MODEL [2009.07.21 09:23:43]


[퀴즈] 조건부 누적 쿼리

다음과 같은 seq, amt 자료가 있을때 result를 구하는 쿼리를 작성하세요.
순차적으로 amt값을 누적합산하되 그 값이 음수일경우엔 0이 되어야 합니다.

WITH t AS
(
SELECT 1 seq, -2000 amt FROM dual
UNION ALL SELECT  2,   4000 FROM dual
UNION ALL SELECT  3,  -5000 FROM dual
UNION ALL SELECT  4,  -2000 FROM dual
UNION ALL SELECT  5,   3000 FROM dual
UNION ALL SELECT  6,   1500 FROM dual
UNION ALL SELECT  7,   -250 FROM dual
UNION ALL SELECT  8,    320 FROM dual
UNION ALL SELECT  9,  -4000 FROM dual
UNION ALL SELECT 10,  10000 FROM dual
UNION ALL SELECT 11, -20000 FROM dual
)
SELECT * FROM t

 

SEQ

AMT

RESULT

계산 방법 참고

1

-2000 0  -2000 이 음수이므로 0

2

4000 4000  0 + 4000 = 4000

3

-5000 0  4000 - 5000 = -1000 = 0

4

-2000 0  0 - 2000 = -2000 = 0

5

3000 3000  0 + 3000 = 3000

6

1500 4500  3000 + 1500 = 4500

7

-250 4250  4500 - 250 = 4250

8

320 4570  4250 + 320 = 4570

9

-4000 570  4570 - 4000 = 570

10

10000 10570  570 + 10000 = 10570

11

-20000 0  10570 - 20000 = -9430 = 0

 

[정답] <=== 트리플클릭
SELECT *
  FROM t
 MODEL DIMENSION BY (seq)
       MEASURES (amt, amt result)
       RULES (result[FOR seq FROM 1 TO 11 INCREMENT 1]
            = GREATEST(0, SUM(result)[seq BETWEEN CV()-1 AND CV()])
              )
;

 [힌트] http://www.gurubee.net/article/12866

by 웅 [2009.07.21 13:04:57]
SELECT SEQ, AMT, CALC
FROM t
MODEL
DIMENSION BY (SEQ)
MEASURES (AMT, AMT RESULT, 0 CALC)
RULES(
CALC[ANY] = PRESENTV(RESULT[CV()-1], CASE WHEN CALC[CV()-1]+ RESULT[CV()] < 0 THEN 0
ELSE CALC[CV()-1]+ RESULT[CV()]
END
, 0)
)
ORDER BY SEQ

by 웅 [2009.07.21 13:15:23]
흠..오류가 하나 있었네요.
SELECT SEQ, AMT, CALC
FROM t
MODEL
DIMENSION BY (SEQ)
MEASURES (AMT, AMT RESULT, 0 CALC)
RULES(
CALC[ANY] = PRESENTV(RESULT[CV()-1], CASE WHEN CALC[CV()-1]+ RESULT[CV()] < 0 THEN 0
ELSE CALC[CV()-1]+ RESULT[CV()]
END
, CASE WHEN RESULT[CV()] >= 0 THEN RESULT[CV()]
ELSE 0
END)
)
ORDER BY SEQ
/

by 마농 [2009.07.21 14:20:28]
웅님 잘 푸셨어요.
웅님께 얻은 힌트를 토대로 좀 더 간단하게 작성해 봤습니다.
SELECT *
FROM t
MODEL DIMENSION BY (seq)
MEASURES (amt, 0 result)
RULES (result[ANY] = GREATEST(0, NVL(result[CV()-1],0) + amt[CV()]))
;

by 마농 [2009.07.27 16:18:59]
ANY 키워드 사용시 데이터의 순서를 바꿔 놓으니 에러가 발생하게 되네요.
순차적 계산을 위해선 for문을 사용해야겠네요.

by 웅 [2009.07.27 17:24:59]
입력row가 몇개라는 걸 알아야 된다는건 좀.. 그냥 인라인뷰에서 정렬하면 문제가 있을까요? ^^a
더 좋은 방법을 찾아봅시다~
SELECT *
FROM (select * from t order by seq)
MODEL DIMENSION BY (seq)
MEASURES (amt, 0 result)
RULES (result[ANY] = GREATEST(0, NVL(result[CV()-1],0) + amt[CV()]))

by 웅 [2009.07.30 14:50:17]
SELECT *
FROM t
MODEL DIMENSION BY (seq)
MEASURES (amt, 0 result)
RULES automatic order --이런 옵션이 있네요 ^^;
(result[ANY] = GREATEST(0, NVL(result[CV()-1],0) + amt[CV()]))

by 마농 [2009.07.30 14:56:25]
오홍~좋아요...
한글로 잘 정리된거 없나? 영어로 된 메뉴얼 너무 보기 힘들어요~
쫘악 한페이지에 정리된것도 아니고 따로따로 되어 있는거 계속 찾아 들어가야 하고....

by 열심히 [2009.07.30 17:27:45]
이해가 안가는 부분이 있는데요..

(result[ANY] = GREATEST(0, NVL(result[CV()-1],0) + amt[CV()])) 에서

result[ANY] 가 뜻하는것과, CV()-1 이부분이 어떻게 동작을 하는건지 힌트문서를 읽어봐도 잘 이해가 안되네요..

설명 좀 부탁 드려도 될까요??

by 손님 [2009.07.31 09:52:07]
위의 쿼리를 실행하면요..

ORA-37002: Oracle OLAP이 초기화를 실패했습니다. Oracle OLAP Technical Support로 문의하십시오.
ORA-33262: 분석 작업 영역 EXPRESS이(가) 존재하지 않습니다.

에러가 나는데요..

특별히 뭐가 설치되어 있어야 하나요???

by 웅 [2009.07.31 10:15:43]
그림을 통해서 배열을 설명하면 이해가 쉬우실 것 같습니다만..
일단 코드비교를 통해서 설명해보겠습니다.
단순히 rules에서 좌변의 의미를 단순한 쿼리로 비교하자면 다음과 같습니다.

select seq, amt, 999 result
from t;

select seq, amt, result
from t
model
dimension by (seq)
measures (amt, 0 result)
rules ( result[any] = 999);

한가지 다른 예로는
select seq, amt, case when seq between 1 and 5 then 999 else 0 end result
from t;

select seq, amt, result
from t
model
dimension by (seq)
measures (amt, 0 result)
rules ( result[seq between 1 and 5] = 999);

이렇게 rules의 좌변은 값을 적용할 범위를 지정합니다.

cv()함수는 배열의 현재 포인터(?)가 위치한 값을 가지고 오는 함수입니다. 우변에만 사용할 수 있습니다.
cv()-1은 현재 배열값에서 1을 뺀 값입니다.

값이 제공되면 dimension by 에 의해서 seq값을 첨자로 배열이 만들어집니다.(이부분이 굉장히 중요합니다. 이해도 어렵고요.)
배열인자(seq)은 1부터 11까지밖에 없는데 0은 범위밖에 있기때문에 null을 반환합니다.

첫번째row result[1-1] = null을 0으로 리턴, amt[1] = -2000, 결과값이 0보다 작으니 result[1]에는 0이 대입됩니다.
두번째row result[2-1]는 0, amt[2] = 4000, result[2] = 4000
...

저도 겨우 얼마전부터 model절을 알아가고 있어서 틀린부분이 있을 수도 있습니다만 제가 아는 바를 최대한 간단히 설명해보았습니다.


by 러드 [2009.07.31 13:50:29]
이거 쿼리로는 힘들까요??? 잘 안되네요;;;

그런데 이 부분은 뭘 찾아봐야 합니까:??

by 마농 [2009.07.31 14:50:24]
절차적 처리가 있어야만 해결되는 요구사항입니다.
전체적으로 일관된 규칙이 적용되는 것이 아니라
순차적으로 규칙이 적용되면서 그결과를 다음 순서에서 참조하는 형태입니다.
즉 앞단의 결과가 뒷단에 영향을 미치는 구조입니다.
Sql은 절차적 언어가 아니고 집합적, 구조적 언어이기 때문에 일반 Sql 로는 불가능하리라 생각되네요.

by 열심히 [2009.07.31 16:07:41]
아..웅님 답글 감사드립니다..

이제야 좀 이해가 되었습니다..^^...감사합니다...^^

by 마농 [2009.08.13 13:19:19]
IGNORE NAV 옵션을 사용하면 NVL 처리를 안해도 되네요.

SELECT *
FROM t
MODEL DIMENSION BY (seq)
MEASURES (amt, 0 result)
IGNORE NAV
RULES AUTOMATIC ORDER
(result[ANY] = GREATEST(0, result[CV()-1] + amt[CV()]))
ORDER BY seq
;

by 러드 [2009.08.17 10:13:56]
여기에 내용은 무엇을 찾아 보면 될까요??

by 마농 [2009.08.17 10:25:02]

by 마농 [2012.04.05 18:00:38]
11G Recursive SQL(재귀쿼리) 을 이용하니 Model 절이 아니어도 가능하군요.
WITH t AS
(
SELECT 1 seq, -2000 amt FROM dual
UNION ALL SELECT  2,   4000 FROM dual
UNION ALL SELECT  3,  -5000 FROM dual
UNION ALL SELECT  4,  -2000 FROM dual
UNION ALL SELECT  5,   3000 FROM dual
UNION ALL SELECT  6,   1500 FROM dual
UNION ALL SELECT  7,   -250 FROM dual
UNION ALL SELECT  8,    320 FROM dual
UNION ALL SELECT  9,  -4000 FROM dual
UNION ALL SELECT 10,  10000 FROM dual
UNION ALL SELECT 11, -20000 FROM dual
)
, t1(seq, amt, result) AS
(
SELECT seq, amt
     , GREATEST(0, amt) result
  FROM t
 WHERE seq = 1
 UNION ALL
SELECT b.seq, b.amt
     , GREATEST(0, a.result + b.amt) result
  FROM t1 a, t b
 WHERE a.seq + 1 = b.seq
)
SELECT * FROM t1
;
댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입