[퀴즈] 8.0 버전용 랭킹쿼리 작성 0 12 5,053

by 마농 rank [2010.02.16 10:50:31]


[퀴즈] 8.0 버전용 랭킹쿼리 작성

[요구사항]
사원테이블에서 사원의 점수에 대한 전체 순위와 부서별 순위를 구하고
부서코드, 부서별순위, 사원번호 순으로 정렬하여 조회하세요.

개발자 마농군은 위 요구사항에 따라 개발환경(10G)에서 다음과 같은 랭킹쿼리를 작성하였습니다.
스스로 짠 쿼리에 만족감을 느끼며 쿼리를 운영환경으로 이관하였더랍니다.
그러나 실제 운영환경(8.0.6)에서는 쿼리가 동작하지 않았습니다. OTL...
분석함수는 8.1.6 부터 적용이 되더군요.
좌절하고 있는 마농군을 위해 8.0 버전용 쿼리를 작성해 주세요.

CREATE TABLE test_rank
AS
SELECT 1 empno, 10 deptno, 100 point FROM dual
UNION ALL SELECT 2, 10,  90 FROM dual
UNION ALL SELECT 3, 10,  80 FROM dual
UNION ALL SELECT 4, 20, 100 FROM dual
UNION ALL SELECT 5, 20,  90 FROM dual
UNION ALL SELECT 6, 20,  80 FROM dual
UNION ALL SELECT 7, 30,  95 FROM dual
UNION ALL SELECT 8, 30,  85 FROM dual
UNION ALL SELECT 9, 30,  95 FROM dual
;

SELECT empno
, deptno
, point
, RANK() OVER(ORDER BY point DESC) rk_all
, RANK() OVER(PARTITION BY deptno ORDER BY point DESC) rk_dept
  FROM test_rank
 ORDER BY deptno, rk_dept, empno
;

[결과]

EMPNO DEPTNO POINT RK_ALL RK_DEPT
1 10 100 1 1
2 10 90 5 2
3 10 80 8 3
4 20 100 1 1
5 20 90 5 2
6 20 80 8 3
7 30 95 3 1
9 30 95 3 1
8 30 85 7 3

정답은 몇일 후에 올리겠습니다.

[정답보기] <== 트리플클릭
SELECT a.empno
, a.deptno
, a.point
, COUNT(b.empno) + 1 rk_all
, COUNT(DECODE(a.deptno,b.deptno,1)) + 1 rk_dept
  FROM test_rank a
, test_rank b
 WHERE a.point < b.point(+)
 GROUP BY a.empno, a.deptno, a.point
 ORDER BY deptno, rk_dept, empno
;

by Ejql [2010.02.17 09:48:49]
SELECT a.*, (select count(*) + 1 from test_rank b where a.point < b.point) rk_all,
(select count(*) + 1 from test_rank b where a.deptno= b.deptno and a.point < b.point) rk_dept
from test_rank a
;


by 마농 [2010.02.17 10:08:25]
아! 안타깝게도 8.0 버전에서는 스칼라서브쿼리가 안됩니다.

by Ejql [2010.02.17 10:30:29]
아. 이런. 8버전이 없어서 테스트가 잘안되서. 다시 해야겠네요.

by 종이 [2010.02.17 15:26:26]
select a.empno, a.deptno, a.point, b.rnum, d.rnum - c.dnum + 1 drnum
from test_rank a,
(
select min(rownum) rnum, point
from
(
select empno, deptno, point
from test_rank
order by point desc
)
group by point
) b,
(
select min(rownum) dnum, deptno
from test_rank
group by deptno
order by deptno
) c,
(
select min(rownum) rnum, deptno, point
from
(
select empno, deptno, point
from test_rank
order by deptno asc, point desc
)
group by deptno, point
) d
where a.point = b.point
and a.deptno = c.deptno
and a.deptno = d.deptno
and a.point = d.point
order by a.deptno, d.rnum - c.dnum + 1, a.empno;

by 마농 [2010.02.17 15:36:13]
아! 8.0에선 안되는게 너무 많네요...
인라인뷰 안에서는 order by를 사용할 수 없습니다.

by 종이 [2010.02.17 16:18:22]
재밋게 풀어봤는데 틀렸네요. 아쉽습니다. 그리고 ... 감사합니다.

by Ejql [2010.02.17 17:19:08]
이것은 되려나요?
select x.*, y.rk_all, z.rk_dept
from
test_rank x
,
(
SELECT a.empno,count(b.empno)+1 rk_all
from test_rank a, test_rank b
where a.point < b.point(+)
group by a.empno, a.point ) y
,
(
SELECT a.deptno,a.empno, a.point, count(b.empno)+1 rk_dept
from test_rank a, test_rank b
where a.point < b.point(+) and a.deptno = b.deptno(+)
group by a.deptno,a.empno, a.point) z
where
x.empno = y.empno
and x.empno = z.empno
order by x.empno, x.deptno
;

by 마농 [2010.02.17 17:39:46]
잘 하셨습니다.
x, y, z 3개의 집합으로 나누어 다시 조인하셨는데요.
각 집합에는 공통 분모가 존재합니다.
굳이 3개로 나누지 않고 한번에 될 수 있습니다.
y 집합만 가지고 잘만 응용하시면 정답에 근접할 수 있을것 같네요.

by Ejql [2010.02.17 17:57:54]
답나오기 바뻐서 필요없는 부분있는걸 몰랐네요. 감사합니다.

select y.empno, z.deptno, z.point, y.rk_all, z.rk_dept
from
(
SELECT a.empno,count(b.empno)+1 rk_all
from test_rank a, test_rank b
where a.point < b.point(+)
group by a.empno, a.point ) y
,
(
SELECT a.deptno,a.empno, a.point, count(b.empno)+1 rk_dept
from test_rank a, test_rank b
where a.point < b.point(+) and a.deptno = b.deptno(+)
group by a.deptno,a.empno, a.point) z
where
y.empno = z.empno
order by y.empno, z.deptno
;

by 손님 [2010.05.07 13:51:17]
WITH TEST_RANK
AS
(
SELECT 1 EMPNO, 10 DEPTNO, 100 POINT FROM DUAL
UNION ALL SELECT 2, 10, 90 FROM DUAL
UNION ALL SELECT 3, 10, 80 FROM DUAL
UNION ALL SELECT 4, 20, 100 FROM DUAL
UNION ALL SELECT 5, 20, 90 FROM DUAL
UNION ALL SELECT 6, 20, 80 FROM DUAL
UNION ALL SELECT 7, 30, 95 FROM DUAL
UNION ALL SELECT 8, 30, 85 FROM DUAL
UNION ALL SELECT 9, 30, 95 FROM DUAL
)
SELECT B.EMPNO,
B.DEPTNO,
B.POINT,
COUNT(DECODE(A.POINT,B.POINT,DECODE(SIGN(A.EMPNO-B.EMPNO),-1,1,0,1),1)) ROW_NB,
COUNT(DISTINCT A.POINT) DENSE_RN,
COUNT(DECODE(A.POINT,B.POINT,NULL,1))+1 RANK,
COUNT(DECODE(A.POINT,B.POINT,NULL,DECODE(A.DEPTNO,B.DEPTNO,1)))+1 RANK_DEPT
FROM TEST_RANK A, TEST_RANK B
WHERE A.POINT >= B.POINT
GROUP BY B.EMPNO, B.DEPTNO, B.POINT
ORDER BY B.DEPTNO, RANK_DEPT;

ROW_NB : ROW_NUMBER
DENSE_RN : DENSE_RANK
RANK : RANK

입니다~

by 손님. [2010.05.07 13:56:07]
위에것 비교하시면서 보기 편하도록.. ^^;

WITH TEST_RANK
AS
(
SELECT 1 EMPNO, 10 DEPTNO, 100 POINT FROM DUAL
UNION ALL SELECT 2, 10, 90 FROM DUAL
UNION ALL SELECT 3, 10, 80 FROM DUAL
UNION ALL SELECT 4, 20, 100 FROM DUAL
UNION ALL SELECT 5, 20, 90 FROM DUAL
UNION ALL SELECT 6, 20, 80 FROM DUAL
UNION ALL SELECT 7, 30, 95 FROM DUAL
UNION ALL SELECT 8, 30, 85 FROM DUAL
UNION ALL SELECT 9, 30, 95 FROM DUAL
)
SELECT A.EMPNO,
A.DEPTNO,
A.POINT,
ROW_NUMBER() OVER (ORDER BY A.POINT DESC, A.EMPNO ASC) RN,
CNT RN1,
DENSE_RANK() OVER (ORDER BY A.POINT DESC) DENSE_RK,
CNT3 DENSE_RK1,
RANK() OVER (ORDER BY A.POINT DESC) RK,
B.CNT1+1 RN1,
RANK() OVER(PARTITION BY DEPTNO ORDER BY POINT DESC) RK_DEPT,
B.CNT2+1 RK_DEPT1
FROM TEST_RANK A, (SELECT B.EMPNO,
COUNT(DECODE(A.POINT,B.POINT,DECODE(SIGN(A.EMPNO-B.EMPNO),-1,1,0,1),1)) CNT,
COUNT(DISTINCT A.POINT) CNT3,
COUNT(DECODE(A.POINT,B.POINT,NULL,1)) CNT1,
COUNT(DECODE(A.POINT,B.POINT,NULL,DECODE(A.DEPTNO,B.DEPTNO,1))) CNT2
FROM TEST_RANK A, TEST_RANK B
WHERE A.POINT >= B.POINT
GROUP BY B.EMPNO
) B
WHERE A.EMPNO = B.EMPNO
ORDER BY A.DEPTNO, RN, EMPNO;

by 손님 [2010.10.05 11:07:26]
마농님 대단하시다. 발상자체가 다르네
댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입