근무 시작시간과 근무시간 입력 받아 근무 종료시간 반환하는 함수

* 문제

근무 시작시간(i_t)과 총소요시간(i_h)을 입력 받아 근무 종료시간(o_t)을 반환하는
사용자 함수(f_Quiz)를 생성하는 문제


* 가정

    - 통상 근무시간은 평일 오전 9시부터 오후 6시
    - 오후 12시부터 1시는 점심시간으로 근무시간에서 제외
    - 토, 일요일 및 휴일도 근무시간에서 제외
    - 휴일은 별도의 테이블로 관리






<리스트 1> 휴일 테이블 생성 및 조회 CREATE TABLE t_holiday AS SELECT '20140101' dt FROM dual UNION ALL SELECT '20140130' FROM dual UNION ALL SELECT '20140131' FROM dual UNION ALL SELECT '20140201' FROM dual UNION ALL SELECT '20140301' FROM dual UNION ALL SELECT '20140505' FROM dual UNION ALL SELECT '20140506' FROM dual UNION ALL SELECT '20140606' FROM dual ; SELECT * FROM t_holiday; DT 20140130 20140131 20140201 20140301 20140505 20140506 20140606

<리스트 2> 사용자 함수 결과 조회 WITH t AS ( SELECT 1 no, '2014/01/01 12:30' t, 2 h FROM dual UNION ALL SELECT 2, '2014/01/30 12:30', 4 FROM dual UNION ALL SELECT 3, '2014/03/04 07:30', 2 FROM dual UNION ALL SELECT 4, '2014/03/04 12:30', 2 FROM dual UNION ALL SELECT 5, '2014/03/04 18:30', 2 FROM dual UNION ALL SELECT 6, '2014/03/04 09:30', 5 FROM dual UNION ALL SELECT 7, '2014/03/07 17:00', 2 FROM dual ) SELECT no, t, h , f_Quiz(t, h) x FROM t ; NO T H X 1 2014-01-01 12:30 2 2014-01-02 11:00 2 2014-01-30 12:30 4 2014-02-03 14:00 3 2014-03-04 07:30 2 2014-03-04 11:00 4 2014-03-04 12:30 2 2014-03-04 15:00 5 2014-03-04 18:30 2 2014-03-05 11:00 6 2014-03-04 09:30 5 2014-03-04 15:30 7 2014-03-07 17:00 2 2014-03-10 10:00



*정답

CREATE OR REPLACE FUNCTION f_Quiz(i_t VARCHAR2, i_h NUMBER) RETURN VARCHAR2 IS d_t DATE := TO_DATE(i_t, 'yyyy/mm/dd hh24:mi'); hh24mi VARCHAR2(5) := TO_CHAR(d_t, 'hh24:mi'); o_t VARCHAR2(16); -- 다음시작시간 o_h NUMBER; -- 다음남은시간 flag NUMBER(1) := 0; -- 재귀호출여부 huil NUMBER(1) := 0; -- 휴일여부 BEGIN -- 휴일여부 가져오기 SELECT COUNT(*) INTO huil FROM t_holiday WHERE dt = TO_CHAR(d_t, 'yyyymmdd') ; IF TO_CHAR(d_t, 'd') IN ('1', '7') OR huil = 1 THEN -- 휴일엔 다음날 아침 9시로 재호출 o_h := i_h; o_t := TO_CHAR(d_t + 1, 'yyyy/mm/dd')||' 09:00'; flag := 1; ELSIF hh24mi < '09:00' THEN -- 평일 9시 이전엔 9시로 재호출 o_h := i_h; o_t := TO_CHAR(d_t, 'yyyy/mm/dd')||' 09:00'; flag := 1; ELSIF hh24mi BETWEEN '09:00' AND '11:59' AND d_t + i_h/24 >= TRUNC(d_t) + 12/24 THEN -- 오전 근무시간중 점심시간 오버하면 -- 오버된 시간만큼 13시로 재호출 o_h := ( (d_t + i_h/24) - (TRUNC(d_t) + 12/24) )*24; o_t := TO_CHAR(d_t, 'yyyy/mm/dd')||' 13:00'; flag := 1;
ELSIF hh24mi BETWEEN '12:00' AND '12:59' THEN -- 점심시간엔 13시로 재호출 o_h := i_h; o_t := TO_CHAR(d_t, 'yyyy/mm/dd')||' 13:00'; flag := 1; ELSIF hh24mi BETWEEN '13:00' AND '17:59' AND d_t + i_h/24 >= TRUNC(d_t) + 18/24 THEN -- 오후 근무시간중 저녁시간 오버하면 -- 오버된 시간만큼 다음날 9시로 재호출 o_h := ( (d_t + i_h/24) - (TRUNC(d_t) + 18/24) )*24; o_t := TO_CHAR(d_t + 1, 'yyyy/mm/dd')||' 09:00'; flag := 1; ELSIF hh24mi >= '18:00' THEN -- 18시 이후엔 다음날 9시로 재호출 o_h := i_h; o_t := TO_CHAR(d_t + 1, 'yyyy/mm/dd')||' 09:00'; flag := 1; END IF; IF flag = 1 THEN o_t := f_Quiz(o_t, o_h); -- 재귀호출 ELSE o_t := TO_CHAR(d_t + i_h/24, 'yyyy/mm/dd hh24:mi'); END IF; RETURN o_t; END; /




* 해설

- 휴일은 다음날 오전 9시 시작
- 평일 9시 이전엔 오전 9시 시작
- 평일 점심시간엔 오후 1시 시작
- 평일 18시 이후엔 다음날 9시 시작
- 나머지 근무시간 중에는 그 시간 그대로 시작


- row 6 사례

1. 시작시간인 9:30은 근무시간 중이므로 그대로 시작
2. 소요시간인 5시간을 적용 시 점심 시간 초과
3. 2시간 30분을 소요한 뒤 다시 13:00에 시작
4. 남은 시간 2시간 30분을 다시 소요하면 15:30으로 결과가 도출


- 부분 분석



1. 함수 선언 및 입력변수 선언 CREATE OR REPLACE FUNCTION f_Quiz(i_t VARCHAR2, i_h NUMBER)
날짜와 시간을 다루는 함수이므로 반복적으로 자주 사용될 날짜변수를 선언하고 초기값을 지정해 줍니다.





2. 자주 쓰는 변수 선언 d_t DATE := TO_DATE(i_t, 'yyyymmddhh24mi'); hh24mi VARCHAR2(4) := TO_CHAR(d_t, 'hh24mi');
다음 시작시간과, 남은 소요시간이 저장될 변수 및 재귀호출여부 변수, 휴일여부 변수를 선언


o_t VARCHAR2(12); -- 다음시작시간 o_h NUMBER; -- 다음남은시간
재귀호출여부 및 휴일여부 변수를 선언하고 초기값을 지정


flag NUMBER(1) := 0; -- 재귀호출여부 huil NUMBER(1) := 0; -- 휴일여부 SELECT COUNT(*) INTO huil FROM t_holiday WHERE dt = TO_CHAR(d_t, 'yyyymmdd') ;



3. 6가지 유형에 따른 조건 분기 IF TO_CHAR(d_t, 'd') IN ('1', '7') OR huil = 1 THEN -- 1. 휴일엔 다음날 아침 8시로 재호출 o_h := i_h; o_t := TO_CHAR(d_t + 1, 'yyyy/mm/dd')||' 09:00'; flag := 1; ELSIF hh24mi < '09:00' THEN -- 2. 평일 9시 이전엔 9시로 재호출 o_h := i_h; o_t := TO_CHAR(d_t, 'yyyy/mm/dd')||' 09:00'; flag := 1; ELSIF hh24mi BETWEEN '09:00' AND '11:59' AND d_t + i_h/24 >= TRUNC(d_t) + 12/24 THEN -- 3. 오전 근무시간중 점심시간 오버하면 -- 오버된 시간만큼 13시로 재호출 o_h := ( (d_t + i_h/24) - (TRUNC(d_t) + 12/24) )*24; o_t := TO_CHAR(d_t, 'yyyy/mm/dd')||' 13:00'; flag := 1; ELSIF hh24mi BETWEEN '12:00' AND '12:59' THEN -- 4. 점심시간엔 13시로 재호출 o_h := i_h; o_t := TO_CHAR(d_t, 'yyyy/mm/dd')||' 13:00'; flag := 1; ELSIF hh24mi BETWEEN '13:00' AND '17:59' AND d_t + i_h/24 >= TRUNC(d_t) + 18/24 THEN -- 5. 오후 근무시간중 저녁시간 오버하면 -- 오버된 시간만큼 다음날 9시로 재호출 o_h := ( (d_t + i_h/24) - (TRUNC(d_t) + 18/24) )*24; o_t := TO_CHAR(d_t + 1, 'yyyy/mm/dd')||' 09:00'; flag := 1; ELSIF hh24mi >= '18:00' THEN -- 6. 18시 이후엔 다음날 8시로 재호출 o_h := i_h; o_t := TO_CHAR(d_t + 1, 'yyyy/mm/dd')||' 09:00'; flag := 1; END IF;
다음 6가지 유형별로 다음시작시간(o_t)과 남은소요시간(o_h)을 산출

1. 휴일엔 다음날 아침 9시로 재호출
2. 평일 9시 이전에는 9시로 재호출
3. 오전 근무시간 중 점심시간을 오버하면 오버된 시간만큼 13시로 재호출
4. 점심시간엔 13시로 재호출
5. 오후 근무시간중 저녁시간 오버하면 오버된 시간만큼 다음날 9시로 재호출
6. 18시 이후엔 다음날 8시로 재호출


<리스트 9> 함수 선언 및 입력변수 선언 IF flag = 1 THEN o_t := f_Quiz(o_t, o_h); -- 재귀호출 ELSE o_t := TO_CHAR(d_t + i_h/24, 'yyyy/mm/dd hh24:mi'); END IF; RETURN o_t;
- 이렇게 산출된 다음시작시간(o_t)과 남은소요시간(o_h)을 이용하여 다시 한번 자기 자신의 함수(f_Quiz)를 호출


- 자기 자신을 호출하는 함수를 재귀함수(Recursive Function)이라 하고 재호출된 함수에서는 같은 작업(유형판별 및 함수 재호출)을 반복


- 6가지 유형에 속하지 않게 될 때(flag = 0) 결과를 반환





출처 : 마이크로소프트웨어 5월호
제공 : DB포탈사이트 DBguide.net

댓글

이 블로그의 인기 게시물

파이참에서 인터프리터 꼬인 경우 처리

경사소실 문제 해결