중복발령에서 겹치는 날 제거하는거 도와주세요~ 0 5 2,668

by 손님 중복발령 중복제거 [2009.02.09 00:17:49]


PERNO   PROJECT   ST_DT           END_DT
0001        A001            20090101     20090105
0001        B001           20090104      20090108
0001        B001           20090111     20090114
0001        C001           20090112     20090130

프로젝트 발령 때문에 질문 드리는데요.
사원을 프로젝트에 발령내는데 중복발령이 가능합니다.
예를 들면, 위에서 0001 이라는 사원이 한달동안 프로젝트 A001,B001,C001 에 발령되어서 일을 했고,
A001 프로젝트에서는 1~5일까지, B001 프로젝트에서는 4~8일까지...
이런식으로 중복되어 발령이 되게 되어 있습니다.
즉, 하루에 한개의 프로젝트만 한게 아니라 몇개의 프로젝트에 발령되어 일할 수 있는 것이죠.
여기서 구하려고 하는것은 이 사원이 한달동안 순수하게 일한 날만을 뽑아내려는 것인데..
중복발령이 되었으므로 중복되는 날은 하루로 치고 계산을 해야됩니다.
그렇게해서 위 사원이 한달동안 일한 날은,
A001 : 1~5 = 5일
B001 : 4~8 = 5일
B001 : 11~14 = 4일
C001 : 12~30 = 19일
합계 : 5 + 5 + 4 + 19
       = 33 - 중복된 날 = ?
각 프로젝트별 일한 날을 모두 더해서 중복된 날수를 제거하면 한달동안 일한 날이 계산이 되어 나오게 되는거죠.
PERNO   WORK_DT
0001    15
0002    20
0003    30

이런식으로 만들어서 급여계산을 하려고 했더니 실력이 없어서 쿼리 하나로는 안되더군요.
그래서 예전에 프로시저로 만들었었는데 생각날때마다 해봐도 힘드네요.
고수님들의 도움을 요청합니다.
꼭 도와주세요^^

by 호야 [2009.02.09 09:22:16]
한달만 뽑으로 실려면.. DUAL 을 이용하시구요,

SELECT PERNO,COUNT(LV)
FROM(SELECT PERNO,LV
FROM T,(SELECT TO_CHAR(20090100+LEVEL) LV FROM DUAL CONNECT BY LEVEL <=31) B
WHERE B.LV BETWEEN A.ST_DT AND A.END_DT
GROUP BY PERNO,LV)
GROUP BY PERNO

전체를 다 뽑아야 된다면 대용량이 될지는 않겠지만
DUAL 테이블을 따로 만드셔서 INDEX 걸어서 하시는게 좋은듯.ㅎ^^;

by 웅 [2009.02.09 09:52:33]
select t.perno, count(distinct s_date) work_dt
from t
, (select to_char(to_date(20090101,'yyyymmdd')-1+level,'yyyymmdd') s_date
from dual
connect by level <= to_char(last_day(to_date(20090101,'yyyymmdd')),'dd')
) copy_t
where s_date between t.start_date and t.end_date
group by t.perno

by Ejql [2009.02.09 13:33:57]

with sam1 as
(select '0001' perno, 'A001' project, '20090101' st_dt, '20090104' end_dt from dual
union all
select '0001' perno, 'B001' project, '20090104' st_dt, '20090108' end_dt from dual
union all
select '0001' perno, 'C001' project, '20090111' st_dt, '20090114' end_dt from dual
union all
select '0001' perno, 'C001' project, '20090116' st_dt, '20090125' end_dt from dual
union all
select '0002' perno, 'C001' project, '20090101' st_dt, '20090110' end_dt from dual
union all
select '0002' perno, 'C001' project, '20090112' st_dt, '20090120' end_dt from dual
)
select min(perno), min(check_flag) st_dt, max(check_flag) end_dt, count(*) work_dt from
(
select a.perno, st_dt + level-1 check_flag from sam1 a
connect by level <= end_dt - st_dt + 1
group by a.perno, a.st_dt + level-1
)
group by perno
order by 1;

3번재로 달아봅니다. 거의 거기서 거기라 비슷하네요.

답은 나오는데요. 결과는 느리네요. 날짜를 길게 잡을수록 계층쿼리가 기하급수적으로 늘어나서 작업량이 많아지고, 시간도 늘어나는건 어쩔수가 없네요.

by catai [2009.02.09 23:29:02]
답변 고맙습니다.
호야님꺼 해봤는데 신기하게 되네요..ㅡㅡ;;
이해는 잘 안되지만 열심히 찾아보고 배워야겠네요.
다들 너무 고맙습니다.^^

by catai [2009.02.10 00:20:25]
호야님 조언대로 테이블 따로 만들어서 다시 해봤습니다.

SELECT PERNO
,COUNT(*) CNT
FROM (SELECT A.PERNO
,B.YMD
FROM PRJ_SALARY A
,COPY_YMD B
WHERE B.YMD BETWEEN A.ST_DT AND A.END_DT
GROUP BY A.PERNO,B.YMD)
GROUP BY PERNO
;
댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입