이전 월분의 DB 데이터를 찾는 쿼리 질문드립니다 0 14 1,982

by Dog발자 [2016.11.07 00:26:22]


DB의 내용은 대략 이렇습니다

컬럼 모두 VARCHAR2 문자열입니다

CREATE_YM EMPLOYEE_CD CD
201611 112033 02
201610 112033 02
201609 112033 02
201608 112033 02
201608 443332 01
201607 112033 01
201606 112033 02
201607 443332 01


 

결과 내용

3개월 이하 331건
3~6개월 12건
7~11개월 3건
12개월 초과 2건

해당 데이터에서 보시면 사원번호 112033가 8월 부터 11월까지 02의 데이터가 연속 됩니다

그럼 조회되는 월분 11월을 기준으로 3개월 이상 02데이터 였기 때문에 3~6개월에 포함 시켜

건수가 카운트되는 식입니다

즉, 만약 02라는 데이터가 조회하려는 월분을 기준으로 같은 사원번호로 7개월간 있었다고 하면

7~11개월로 포함이 되어 해당 컬럼에 카운트가 되는 형식이며 중간에 02 이외의 다른 데이터가

해당 사원의 월분에 있으면 거기서 끝이며 월분은 거기까지 세어지게 됩니다

02라는 코드가 같은 사원번호로 매칭해서 몇 개월간 지속 되었느냐가 주인거 같은데

이전 데이터를 계속 거슬러 올라가서 02인지 확인해서 몇개월 지속 됬는지 가져온다는게 힘드네요..

이렇게 해서 전체데이터를 뽑아내야 하는데 접근법이 힘드네요 ..

힌트 부탁드려도 될까요?

by 도미노 같은 놈 [2016.11.07 09:34:09]

CD 가 2개라면...

EMPLOYEE_CD, CD를 GROUP BY 해서 나온 각각의 MAX(CREATE_YM) 들을 

WHERE 조건에 (CREATE_YM > CD=01의 MAX값) OR (CREATE_YM > CD=02의 MAX값) 하면

마지막 코드값의 카운트 구할 수 있지 않을까요??


by Dog발자 [2016.11.07 10:12:37]
코드는 여러개일수도 있구요
각 사원번호 당 몇개월 연속해서 02라는 데이터가 나왔는지가 서브쿼리로 어떻게 해보려 했는데 어렵네요...ㅠ

by jkson [2016.11.07 11:26:09]
WITH  T
AS (
SELECT '201611' AS CREATE_YM,'112033' AS EMPLOYEE_CD,'02' AS CD FROM DUAL UNION ALL
SELECT '201610','112033','02' FROM DUAL UNION ALL
SELECT '201609','112033','02' FROM DUAL UNION ALL
SELECT '201608','112033','02' FROM DUAL UNION ALL
SELECT '201608','443332','01' FROM DUAL UNION ALL
SELECT '201607','112033','01' FROM DUAL UNION ALL
SELECT '201605','112033','02' FROM DUAL UNION ALL
SELECT '201607','443332','01' FROM DUAL 
)
SELECT CASE WHEN CNT < 3 THEN '3개월이하'
            WHEN CNT >=3 AND CNT <= 6 THEN '3~6개월'
            WHEN CNT >=7 AND CNT <= 11 THEN '7~11개월'
            WHEN CNT >=11  THEN '12개월초과'
       END   GB, COUNT(1) CNT                    
  FROM
    (
    SELECT EMPLOYEE_CD, COUNT(1) CNT
      FROM
        (
        SELECT CREATE_YM, EMPLOYEE_CD, CD, BEFMONCD 
             , SUM(DECODE(CD, BEFMONCD, 0, 1)) OVER(PARTITION BY EMPLOYEE_CD ORDER BY CREATE_YM) FG
          FROM
            (
            SELECT CREATE_YM, EMPLOYEE_CD, CD
            , MAX(CD) OVER(PARTITION BY EMPLOYEE_CD ORDER BY TO_DATE(CREATE_YM||'01','yyyymmdd') 
                           RANGE BETWEEN INTERVAL '1' MONTH PRECEDING AND  INTERVAL '1' MONTH PRECEDING) BEFMONCD 
              FROM T
             WHERE CD = '02'--cd가 02인 것만
            )
            ORDER BY 2,1,3
        )
    GROUP BY EMPLOYEE_CD, FG
    )  
GROUP BY CASE WHEN CNT < 3 THEN '3개월이하'
              WHEN CNT >=3 AND CNT <= 6 THEN '3~6개월'
              WHEN CNT >=7 AND CNT <= 11 THEN '7~11개월'
              WHEN CNT >=11  THEN '12개월초과'
         END                     

 


by 마농 [2016.11.07 13:08:58]

'02' 가 3개월 이어지다가 최종 조회월에 '01' 로 바뀐 경우?
카운트 해야 하나요? 안해야 하나요?
1. 안해야 한다면 최종월 '02' 인 자료만 카운트하면 될 듯 하고...
2. 해야 한다면? 과거연속도 포함인 듯 한데요?
  - 과거 연속구간이 여러개인 경우 각각 카운트 하나요?
  - 아니면 최대 구간 한번만 카운트 하나요?

 

추가로 개월 구분의 경계가 모호하네요.

 - 12개월은 어디에도 포함이 안되네요.


by Dog발자 [2016.11.07 15:31:38]

윗 두분 답변 정말감사합니다

조회년월에 01로 바뀌었다면 카운트가 안됩니다

무조건 조회년월 기준으로 이전월분 데이터에 연속된 02 값이 있어야만 카운트되구요

즉, 조회를 2016년11월로 했다면 해당 년월 기준으로 이 월분을 포함하여 월분이 데이터가 연속 됬는가 안됬는가가 기준이네요 ^^;;

설명이 너무 난해하게 했네요 죄송합니다 ㅠㅜ

문제 풀다보니 정신이 없었네요

 


by 마농 [2016.11.07 15:46:41]
WITH t AS
(
SELECT '201611' create_ym, '112033' employee_cd, '02' cd FROM dual
UNION ALL SELECT '201610', '112033', '02' FROM dual
UNION ALL SELECT '201609', '112033', '02' FROM dual
UNION ALL SELECT '201608', '112033', '02' FROM dual
UNION ALL SELECT '201608', '443332', '01' FROM dual
UNION ALL SELECT '201607', '112033', '01' FROM dual
UNION ALL SELECT '201605', '112033', '02' FROM dual
UNION ALL SELECT '201607', '443332', '01' FROM dual
)
SELECT DECODE(gb, 1, '3개월 이하'
                , 2, '4~6개월'
                , 3, '7~12개월'
                , 4, '12개월 초과'
                ) gb
     , COUNT(*) cnt
  FROM (SELECT employee_cd
             , CASE WHEN MAX(rn) <=  3 THEN 1
                    WHEN MAX(rn) <=  6 THEN 2
                    WHEN MAX(rn) <= 12 THEN 3
                    WHEN MAX(rn) >  12 THEN 4
                END gb
          FROM (SELECT employee_cd
                     , TO_DATE(create_ym, 'yyyymm') ym
                     , ROW_NUMBER() OVER(PARTITION BY employee_cd ORDER BY create_ym DESC) rn
                  FROM t
                 WHERE cd = '02'
                   AND create_ym <= '201611'
                )
         WHERE TO_CHAR(ADD_MONTHS(ym, rn-1), 'yyyymm') = '201611'
         GROUP BY employee_cd
        ) a
 GROUP BY a.gb
 ORDER BY a.gb
;

 


by jkson [2016.11.07 16:10:47]

아 조회한 달 기준이군요. 전체 데이터 기준인 줄 알았네요ㅋ


by Dog발자 [2016.11.07 17:16:05]

아! 정말 감사합니다

그런데 마농님 쿼리를 확인해보니

201611로 조회를 하면 얘를 기준으로 이전에 04가 얼마나 연속되는지를 찾더라구요 ㅠ

제가 설명을 잘못한 부분이니 죄송하단 말씀드리겠습니다

jkson님 쿼리는 제가 DB끈이 짧다보니 이해를 못해서 ㅠ

두분 쿼리 모두 제가 수정해보고 안되서 염치불구 하고 다시 질문드립니다

다시 설명해드리면 201611로 조회를 하면 이전월 201610, 201609, 201608이 02이면

3개월 이하로 되어 카운트1이 됩니다

즉, 월분이 조회년월 201611 전월을 기준으로 연속해서 2016년 10월 9월 8월이 02이고 7월이 01이면 3개월 동안 02가 지속 되었기 때문에 3개월 이하

해당 구분에 카운트 1이 되며 다른 개월수 구분에 중복이 되어선 안됩니다

ex)12개월 초과에 카운트 1이 됬으면 다른 구분에는 포함되면 안됨

인데 201611월 전월을 기준으로 월분이 연속이며 02라는 데이터까지 연속이라는것을 찾아내려니

아주 골때리네요 ㅠ

오늘 하루종일 이것저것 써보는데 되지않아 답답하네요..


by 마농 [2016.11.07 17:31:16]

1. 조회 조건만 바꾸면 됩니다.
  - 변경전 : '201611'
  - 변경후 : '201610'
2. 입력값('201611')을 바꾸지 않고 그대로 이용하려면 조건절만 살짝 바꾸면 됩니다.
  - 변경전 : AND create_ym <= '201611'
  - 변경후 : AND create_ym < '201611'


  - 변경전 : WHERE TO_CHAR(ADD_MONTHS(ym, rn-1), 'yyyymm') = '201611'
  - 변경후 : WHERE TO_CHAR(ADD_MONTHS(ym, rn), 'yyyymm') = '201611'


by jkson [2016.11.07 17:50:35]

제가 만든 쿼리는 전체 데이터 기준으로 연속된 월의 카운트를 구하는 거라 요청하신 내용과 다릅니다^^;

결과 예시가 적어주신 기초 데이터와 달라서 제 생각대로 만들었어요.

(기초 데이터로 조회했을 때 결과를 적어주셨으면 더 정확하게 답변 달아드렸을텐데 말이죠.

제 형편없는 이해력이 물론 제일 큰 원인이겠지만요-0-)

02인 데이터가 1월, 2월, 3월, 4월, 7월, 8월, 10월, 11월 이렇게 있으면

(1월, 2월, 3월, 4월), (7월, 8월), (10월, 11월)

이렇게 묶어서 3~6개월 : 1, 3개월이하 : 2 이렇게 나오는 쿼리예요.

아.. 그리고 3개월이하면 3개월까지 포함인 거죠? 3~6개월에 3이 또 있길래..ㅋㅋ


by Dog발자 [2016.11.07 18:41:04]

아! 피드백 감사드립니다!

jkson님 쿼리도 지금 분석하면서 공부중입니다 ㅎ 감사드립니다!

가상 테이블에서는 저렇게 하는게 맞는데

제 DB에서 작성하신 쿼리의 케이스문 바로 밑의 서브쿼리를 조회 해보니

 

사원번호    년월         RN카운트

102001    2015-10-01    1
102001    2015-09-01    2
112002    2016-10-01    1
112002    2015-12-01    2
112002    2015-11-01    3
112002    2015-06-01    4
112002    2015-05-01    5
112002    2015-03-01    6
112002    2014-09-01    7
112002    2013-01-01    8
112002    2012-11-01    9
112002    2012-03-01    10
112002    2012-02-01    11
112002    2011-07-01    12

 

이런식으로 월분이 연속되지않게 월분관계 없이 02만 연속인지 체크를 하더군요

10월에서 거꾸로 순서대로 가면서 02인지 체크해서 아니면 거기서 스탑!하고 싶은데

RANGE도 써보고 했는데 마음대로 안되네요... 조건절만 뭔가 건드려주면 될거 같기도 하고..

바쁘실텐데 거듭 질문드려 죄송합니다


by 마농 [2016.11.07 18:51:42]

서브쿼리 결과만 보고 잘못 판단하신 듯 하네요?
서브쿼리에서 rn 을 붙인 결과가 최종결과가 아니죠.
그다음 add_month 조건절로 최종결과물을 추출합니다.


사원번호    년월         RN  TO_CHAR(ADD_MONTHS(ym, rn), 'yyyymm')
 102001    2015-10-01     1    201511
 102001    2015-09-01     2    201510
 112002    2016-10-01     1    201611   <--- 최종결과에 포함
 112002    2015-12-01     2    201602
 112002    2015-11-01     3    201602
 112002    2015-06-01     4    201510
 112002    2015-05-01     5    201510
 112002    2015-03-01     6    201509
 112002    2014-09-01     7    201504
 112002    2013-01-01     8    201309
 112002    2012-11-01     9    201308
 112002    2012-03-01    10    201301
 112002    2012-02-01    11    201301
 112002    2011-07-01    12    201207


by jkson [2016.11.07 19:12:42]

마농님 답변대로 해도 원하는 결과가 안 나오신다면

원하시는 바를 잘못 설명하셨든지

아직 쿼리를 제대로 이해 못 하신 게 아닐까요?

 

WITH t AS
(
SELECT '201611' create_ym, '112033' employee_cd, '02' cd FROM dual
UNION ALL SELECT '201610', '112033', '02' FROM dual
UNION ALL SELECT '201609', '112033', '02' FROM dual
UNION ALL SELECT '201608', '112033', '02' FROM dual
UNION ALL SELECT '201608', '443332', '01' FROM dual
UNION ALL SELECT '201607', '112033', '01' FROM dual
UNION ALL SELECT '201605', '112033', '02' FROM dual
UNION ALL SELECT '201607', '443332', '01' FROM dual
)
SELECT employee_cd
     , ym
     , rn
     , TO_CHAR(ADD_MONTHS(ym, rn), 'yyyymm') fg
  FROM (SELECT employee_cd
             , TO_DATE(create_ym, 'yyyymm') ym
             , ROW_NUMBER() OVER(PARTITION BY employee_cd ORDER BY create_ym DESC) rn
          FROM t
         WHERE cd = '02'
           AND create_ym < '201611'
        )

이 쿼리 결과를 보세요.       

역순으로 순번을 준 rn 값과 ym 을 더한 값이 201611이 나온다는 것은

중간에 빈 달이 없다는 것이고 연속된 데이터라는 거죠.    

   

EMPLOYEE_CD    YM    RN    FG

112033    2016-10-01    1    201611 -> 연속
112033    2016-09-01    2    201611 -> 연속
112033    2016-08-01    3    201611 -> 연속
112033    2016-05-01    4    201609 -> 역순으로 4번째인 201605가 4를 더하니 201609가 나왔으므로
3번째 값인 201608과 4번째 값인 201605는 이어진 값이 아님

 

이렇게 11월 이전에 02값을 가진 이어진 값을 찾는 쿼리예요.

원하시는 내용이 이게 맞나요?


by Dog발자 [2016.11.07 20:22:33]

아.. 죄송합니다 ㅠ

맞습니다

jkson님이 말씀한대로네요

제가 안쪽만 보고 잘못판단했네요

저의 부족함 때문에.. 괜한 논란을 불러 일으킨거 같네요..

두분모두 감사합니다!

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입