마농님이 주신 답변인데 문제가 생겼습니다. 0 1 1,902

by 열심히 [2012.04.24 10:22:09]


제가 예전에 아래와 같이 질문을 드렸더니 마농님께서 답변을 주셨는데 문제가 좀 생겼습니다..

[ *  제가 드린 질문 ]
테이블은 아래와 같고 기본키는 (학교/학번/과목/시험방법) 입니다.
-------------------------------------------------------------------------------------
학교 /학번 /과목 /시험방법 /시험시작일 /시험상태
-------------------------------------------------------------------------------------
AAA   001   국어   A   2000-01-01   시험준비
AAA   001   국어   B   2000-03-03   시험준비
AAA   001   국어   C   2000-08-08   시험준비
AAA   001   국어   D   2000-11-11   시험준비
BBB   002   영어   A   2000-02-02   시험완료
BBB   002   영어   B   2000-04-01   시험준비
BBB   002   영어   C   2000-06-06   시험준비
BBB   002   영어   D   2000-08-08   시험준비
CCC   003   일어   A   2000-01-02   시험완료
CCC   003   일어   B   2000-01-03   시험준비
CCC   003   일어   C   2000-01-04   시험준비
CCC   003   일어   D   2000-01-05   시험준비
DDD   003   수학   A   2000-04-29   시험완료
DDD   003   수학   B   2000-05-04   시험완료
DDD   003   수학   C   2000-05-20   시험준비
DDD   003   수학   D   2000-05-30   시험준비
EEE   003   독어   A   2000-01-29   시험완료
EEE   003   독어   B   2000-02-01   시험완료
EEE   003   독어   C   2000-02-08   시험완료
EEE   003   독어   D   2000-02-28   시험완료
FFF   002   사회   A   2000-02-02   시험완료
FFF   002   사회   B   2000-04-01   시험완료
FFF   002   사회   C   2000-06-06   시험준비
FFF   002   사회   D   2000-08-08   시험준비
GGG   002   불어   A   2000-04-08   시험완료
GGG   002   불어   B   2000-05-09   시험완료
GGG   002   불어   C   2000-06-06   시험완료
GGG   002   불어   D   2000-08-08   시험완료
HHH   002   체육   A   2000-02-02   시험완료
HHH   002   체육   B   2000-04-01   시험중
HHH   002   체육   C   2000-06-06   시험준비
HHH   002   체육   D   2000-08-08   시험준비
------------------------------------------------------------------------------------

시험방법은 과목당 A, B, C, D 방법 네종류가 존재합니다.
따라서 한과목당 4개의 레코드가 항상존재합니다.
시험상태는 시험준비, 시험중, 시험완료 중 하나가 됩니다.
A방법 - (시험준비, 시험중, 시험완료) 중 하나
B방법 - (시험준비, 시험중, 시험완료) 중 하나 - A가 시험완료가 되어야 B가 시험중 , 시험완료로 입력가능
C방법 - (시험준비, 시험중, 시험완료) 중 하나 - B가 시험완료가 되어야 C가 시험중 , 시험완료로 입력가능
D방법 - (시험준비, 시험중, 시험완료) 중 하나 - C가 시험완료가 되어야 D가 시험중 , 시험완료로 입력가능
* 위 테이블에 국어 과목에 보시듯 테이블에 모든 레코드에 시작할때 4쌍에 대해
시험계획일자를 입력하고 상태는 시험준비상태로 모두 입력을 해 놓습니다.


이제 누군가 시험을 보기 시작했다면
위 테이블에 영어에서 보시듯 영어에 A시험방법을 완료했다면 상태값은 [시험완료]로 업데이트가 되고
B, C, D는 시험준비 그대로 입니다.
B시험이 진행중이라면 상태는 [시험중]이 됩니다. 아직 C, D는 아무것도 안했으니 시험준비로 남아 있게 됩니다.
이런 식으로 상태값은 진행을 합니다.
( * 중요한것은 A단계가 완료가 되어야 B단계가 진행 될 수 있고 C, D 역시 마찬가지입니다.
A시험방법이 완료가 안되었는데 B가 진행중이나 완료가 될 수 없고
B가 완료가 안되었는데 C가 진행중이나 완료가 될 수 없다는 뜻입니다.
각 시험방법은 순차적으로 진행이 됩니다.)


이제 하고 싶은작업은 어떤 사람이 특정일로 조회를 했을때
이 입력일을 기준으로 각 과목에 시험진행상태가 어떻게 되었는가 파악하는 부분입니다.
잘 진행되고 있는지 완료되었는지 지연되고 있는지입니다.
* 실제 시험의 완료란 시험방법 D 까지 완료가 되면 그것은 시험완료입니다.


결과값은 4쌍의 내용을 가로로 펼치고 전체상태만 판단해서 보여줍니다.
---------------------------------------------------------------------------------------------
학교...학번...과목...시험방법A상태...시험방법B상태...시험방법C상태...시험방법D상태...전체상태
---------------------------------------------------------------------------------------------
AAA...001...국어...시험준비...시험준비...시험준비...시험준비...지연
BBB...002...영어...시험완료...시험준비...시험준비...시험준비...시험준비
CCC...003...일어...시험완료...시험준비...시험준비...시험준비...지연
DDD...003...수학...시험완료...시험완료...시험준비...시험준비...시험준비
EEE...003...독어...시험완료...시험완료...시험완료...시험완료...시험완료
FFF...003...사회...시험완료...시험완료...시험준비...시험준비...시험준비
GGG...003...불어...시험완료...시험완료...시험완료...시험완료...시험완료
HHH...003...체육...시험완료...시험중...시험완료...시험완료...시험중
----------------------------------------------------------------------------------------------


어떤 사람이 2000-04-04 을 기준으로 조회를 했다면
국어 과목의 경우 "지연" 입니다.
A시험방법은 2000-01-01 ~ 2000-03-02 시험준비   <-- 2000-04-04기준으로 이미 완료여야 하는데 시험준비므로 "지연"
B시험방법은 2000-03-03 ~ 2000-08-07 시험준비   <-- 2000-04-04기준
C시험방법은 2000-08-08 ~ 2000-11-10 시험준비
D시험방법은 2000-11-11 ~  시험준비


위에 영어 과목은 "시험준비" 상태입니다. 
A시험방법은 2000-02-02 ~ 2000-03-31 시험완료   <-- 2000-04-04기준으로 차질없이 이미 완료되었으며
B시험방법은 2000-04-01 ~ 2000-06-05 시험준비   <-- 2000-04-04기준으로 A는 시험이끝났음. 차질없이 "시험준비"   
C시험방법은 2000-06-06 ~ 2000-08-08 시험준비
D시험방법은 2000-08-08 ~  시험준비


일어의 경우 "지연" 입니다.
A시험방법은 2000-01-02 ~ 2000-01-02 시험완료  
B시험방법은 2000-01-03 ~ 2000-01-03 시험준비   
C시험방법은 2000-01-04 ~ 2000-01-04 시험준비
D시험방법은 2000-01-05 ~  시험준비     <-- 2004-04-04 한참 전인데 아직도 완료되지 않았으므로 "지연"


수학의 경우 "시험준비" 입니다.
A시험방법은 2000-04-29 ~ 2000-05-03 시험완료  <-- 2004-04-04 이후이고 C, D 단계가 아직 준비중인게 있으므로 "시험준비" 
B시험방법은 2000-05-04 ~ 2000-05-19 시험완료   
C시험방법은 2000-05-20 ~ 2000-05-29 시험준비
D시험방법은 2000-05-30 ~  시험준비
  

독어의 경우 "시험완료" 입니다.
A시험방법은 2000-01-29 ~ 2000-01-31 시험완료 
B시험방법은 2000-02-01 ~ 2000-02-07 시험완료   
C시험방법은 2000-02-08 ~ 2000-02-27 시험완료
D시험방법은 2000-02-28 ~  시험완료    <-- 2004-04-04 모두 이전이고 D까지 완료이므로 "시험완료" 


사회의 경우는 시험준비중이겠지요
A시험방법은 2000-02-02 ~ 2000-03-31 시험완료
B시험방법은 2000-04-01 ~ 2000-06-05 시험완료  <-- 2004-04-04 기준으로완료되었고 C, D가 준비이므로 "시험준비"
C시험방법은 2000-06-06 ~ 2000-08-07 시험준비
D시험방법은 2000-08-08 ~  시험준비    


불어의 경우 "시험완료"입니다.
A시험방법은 2000-04-08 ~ 2000-05-08 시험완료  <-- 2004-04-04 이후 D까지 완료이므로 "시험완료"
B시험방법은 2000-05-09 ~ 2000-06-05 시험완료
C시험방법은 2000-06-06 ~ 2000-08-07 시험완료
D시험방법은 2000-08-08 ~  시험완료


체육 과목은 "시험중" 상태입니다. 
A시험방법은 2000-02-02 ~ 2000-03-31 시험완료   <-- 2000-04-04기준으로 차질없이 이미 완료되었으며
B시험방법은 2000-04-01 ~ 2000-06-05 시험중   <-- 2000-04-04기준으로 A는 시험이끝났음. 차질없이 "시험중"   
C시험방법은 2000-06-06 ~ 2000-08-08 시험준비
D시험방법은 2000-08-08 ~  시험준비


위와 같이 질문을 드렸더니 ANSI 표준으로 답을 받았습니다.
[ * 마농님이 주신 답변 ] ************************************************************************************
WITH t(학교, 학번, 과목, 시험방법, 시험시작일, 시험상태) AS
(
SELECT 'AAA', '001', '국어', 'A', '2000-01-01', '시험준비'
UNION ALL SELECT 'AAA', '001', '국어', 'B', '2000-03-03', '시험준비'
UNION ALL SELECT 'AAA', '001', '국어', 'C', '2000-08-08', '시험준비'
UNION ALL SELECT 'AAA', '001', '국어', 'D', '2000-11-11', '시험준비'
UNION ALL SELECT 'BBB', '002', '영어', 'A', '2000-02-02', '시험완료'
UNION ALL SELECT 'BBB', '002', '영어', 'B', '2000-04-01', '시험준비'
UNION ALL SELECT 'BBB', '002', '영어', 'C', '2000-06-06', '시험준비'
UNION ALL SELECT 'BBB', '002', '영어', 'D', '2000-08-08', '시험준비'
UNION ALL SELECT 'CCC', '003', '일어', 'A', '2000-01-02', '시험완료'
UNION ALL SELECT 'CCC', '003', '일어', 'B', '2000-01-03', '시험준비'
UNION ALL SELECT 'CCC', '003', '일어', 'C', '2000-01-04', '시험준비'
UNION ALL SELECT 'CCC', '003', '일어', 'D', '2000-01-05', '시험준비'
UNION ALL SELECT 'DDD', '003', '수학', 'A', '2000-04-29', '시험완료'
UNION ALL SELECT 'DDD', '003', '수학', 'B', '2000-05-04', '시험완료'
UNION ALL SELECT 'DDD', '003', '수학', 'C', '2000-05-20', '시험준비'
UNION ALL SELECT 'DDD', '003', '수학', 'D', '2000-05-30', '시험준비'
UNION ALL SELECT 'EEE', '003', '독어', 'A', '2000-01-29', '시험완료'
UNION ALL SELECT 'EEE', '003', '독어', 'B', '2000-02-01', '시험완료'
UNION ALL SELECT 'EEE', '003', '독어', 'C', '2000-02-08', '시험완료'
UNION ALL SELECT 'EEE', '003', '독어', 'D', '2000-02-28', '시험완료'
UNION ALL SELECT 'FFF', '002', '사회', 'A', '2000-02-02', '시험완료'
UNION ALL SELECT 'FFF', '002', '사회', 'B', '2000-04-01', '시험완료'
UNION ALL SELECT 'FFF', '002', '사회', 'C', '2000-06-06', '시험준비'
UNION ALL SELECT 'FFF', '002', '사회', 'D', '2000-08-08', '시험준비'
UNION ALL SELECT 'GGG', '002', '불어', 'A', '2000-04-08', '시험완료'
UNION ALL SELECT 'GGG', '002', '불어', 'B', '2000-05-09', '시험완료'
UNION ALL SELECT 'GGG', '002', '불어', 'C', '2000-06-06', '시험완료'
UNION ALL SELECT 'GGG', '002', '불어', 'D', '2000-08-08', '시험완료'
UNION ALL SELECT 'HHH', '002', '체육', 'A', '2000-02-02', '시험완료'
UNION ALL SELECT 'HHH', '002', '체육', 'B', '2000-04-01', '시험중'
UNION ALL SELECT 'HHH', '002', '체육', 'C', '2000-06-06', '시험준비'
UNION ALL SELECT 'HHH', '002', '체육', 'D', '2000-08-08', '시험준비'
)
SELECT 학교, 학번, 과목
, MIN(시험상태_A) 시험상태_A
, MIN(시험상태_B) 시험상태_B
, MIN(시험상태_C) 시험상태_C
, MIN(시험상태_D) 시험상태_D
, ISNULL(MAX(
   CASE WHEN 시험상태 != '시험완료'
    THEN CASE WHEN 시험종료일 <= '2000-04-04' THEN '지연'
  WHEN 시험종료일 > '2000-04-04' THEN 시험상태
  END
    END
   ), '시험완료') AS 시험상태
  FROM (
    SELECT 학교, 학번, 과목
, CASE 시험방법 WHEN 'A' THEN 시험상태 END AS 시험상태_A
, CASE 시험방법 WHEN 'B' THEN 시험상태 END AS 시험상태_B
, CASE 시험방법 WHEN 'C' THEN 시험상태 END AS 시험상태_C
, CASE 시험방법 WHEN 'D' THEN 시험상태 END AS 시험상태_D
, 시험상태
, 시험방법
, 시험시작일
, ISNULL(
   (SELECT TOP 1 b.시험시작일
FROM t b
    WHERE b.학교 = a.학교
  AND b.학번 = a.학번
  AND b.과목 = a.과목
  AND b.시험시작일 > a.시험시작일
    ORDER BY 과목
    ), '9999-12-31') AS 시험종료일
  FROM t a
    ) x
 GROUP BY 학교, 학번, 과목
 ORDER BY 학교, 학번, 과목
;

[ * 문제점 ] : 각 시험방법 A, B, C, D 의  일자가 겹치지 않을 경우 위에 로직은 잘 맞았습니다.
그런데 문제는 사용자가 일자를 같은 날로 모두 한꺼번에 넣을수도 있고 ,
A, B, C, D 시험방법에 일자가 중복될수도 있다고 합니다.

위에 주신 답변 쿼리에 데이타를 아래와 같이 바꾸어서 해 보면

WITH t(학교, 학번, 과목, 시험방법, 시험시작일, 시험상태) AS
(
  SELECT 'AAA', '001', '국어', 'A', '2000-01-01', '시험준비'  --> '2000-01-01'
UNION ALL SELECT 'AAA', '001', '국어', 'B', '2000-01-01', '시험준비'  --> '2000-01-01' 
UNION ALL SELECT 'AAA', '001', '국어', 'C', '2000-01-01', '시험준비'  --> '2000-01-01' 
UNION ALL SELECT 'AAA', '001', '국어', 'D', '2000-01-01', '시험준비'  --> '2000-01-01' 
UNION ALL SELECT 'BBB', '002', '영어', 'A', '2000-01-01', '시험완료'
UNION ALL SELECT 'BBB', '002', '영어', 'B', '2000-02-01', '시험중'  --> '2000-02-01'
UNION ALL SELECT 'BBB', '002', '영어', 'C', '2000-02-01', '시험준비'  --> '2000-02-01'
UNION ALL SELECT 'BBB', '002', '영어', 'D', '2000-02-01', '시험준비'  --> '2000-02-01'
UNION ALL SELECT 'CCC', '003', '일어', 'A', '2000-01-02', '시험완료'
UNION ALL SELECT 'CCC', '003', '일어', 'B', '2000-01-02', '시험완료'
UNION ALL SELECT 'CCC', '003', '일어', 'C', '2000-01-03', '시험중'
UNION ALL SELECT 'CCC', '003', '일어', 'D', '2000-01-03', '시험준비'
)

1.  조회 기준일을 '2000-04-04' 일로 했을 경우
-- 국어의 경우 4가지 시험방법일이 모두 '2000-01-01' 이므로 '지연' 이 나와야 하는데 '시험준비'가 나옵니다.
-- 영어의 경우 B, C, D 시험방법일이 '2000-02-01' 이므로 '지연'이 나와야 하는데 '시험중'이 나옵니다.
-- 일어의 경우도 '지연' 이 나와야 하는데 '시험중'가 나옵니다.

2. 조회기준일을 '2000-02-01' 일로 했을 경우
-- 국어의 경우 4가지 시험방법일을 모두 '2000-01-01' 이므로 '지연' 이 나와야 하는데 '시험준비'가 나옵니다.
-- 영어의 경우 '2000-02-01'로 이므로 '시험중' 이라고 제대로 나옵니다.
-- 일어의 경우 '지연'이 나와야 하는데 '시험중'이 나옵니다.

3. 조회기준일을 '2000-01-03' 로 했을 경우
-- 국어의 경우 4가지 시험방법일을 모두 '2000-01-01' 이므로 '지연' 이 나와야 하는데 '시험준비'가 나옵니다.
-- 영어의 경우 '2000-02-01'로 이므로 '시험중' 이라고 제대로 나옵니다.
-- 일어의 경우 '시험중'라고 제대로 나옵니다.

4. 검색일자를 '1999-01-01' 로 했을 경우는 이상이 없습니다.
검색일자 이전에 데이타 일자가 중복이 되는 경우 문제가 있는것 같습니다.

어떻게 고쳐보려고 했는데 답이 안보이네요 . 조언 부탁드립니다.

by 마농 [2012.04.24 10:51:57]
일단 제가 작성해 드렸던 쿼리도 오류가 있었네요.
[ORDER BY 과목] 이라고 한 부분이 지금보니 터무니없네요 ^^a
[ORDER BY 시험시작일] 또는 [ORDER BY 시험방법] 이 되었어야 맞네요.
그건 그렇고 날짜가 중복된다면 ISNULL 안의 서브쿼리를 다음과 같이 바꿔보세요.
(
SELECT TOP 1 b.시험시작일 
  FROM t b 
 WHERE b.학교 = a.학교 
   AND b.학번 = a.학번 
   AND b.과목 = a.과목 
   AND b.시험방법 > a.시험방법
 ORDER BY b.시험방법
)
댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입