728x90
반응형

통계 쿼리는 조금 어려울 수도 쉬울 수도 있을 것 같습니다

하지만 대부분의 사람들은 어렵다고 할 것입니다

저 또한 그렇게 느끼고 있고요

 

한번 같이 공부하면서 알아보도록 하겠습니다

 

 

결과셋을 하나의 행으로 데이터 조회하기

 

통계 쿼리의 가장 기본인 결과셋을 하나의 행으로 만드는 것을 우선 해보겠습니다

행 그룹에서 값을 가져와서 그룹당 단일 행의 열로 변환하려고 합니다.

예를 들어 각 부서의 사원수를 표시하는 결과 셋이 있다고 해봅시다

 

 

아래와 같은 결과 셋을 하나행으로 보이도록 출력을 다시 재구성하려고 합니다.

통계쿼리1
결과셋을 하나의행으로 만들어보자

 

이 방법은 통계 쿼리에 자주 사용하는 방법이며 

가장 기초적인 방법의 예문으로 만들려고 합니다

 

이것을 바탕으로 응용해서 적용해보시길 바랍니다

 

 

우선 테이블에 어떤 데이터가 들어있는지 한번 확인해봅시다.

 

SELECT deptno FROM emp;

 

아래와 같이 데이터가 들어있으며, 총 14개의 행이 있습니다

 

통계쿼리2
원본 테이블 데이터 정보

 

이 데이터를 각 부서의 사원수를 구하기 위해서는

아래와 같이 일반적으로 생각하게 될 것입니다

하지만 이 데이터는 한 행에 출력이 않아 통계 쿼리로는 부적합합니다

그래서 하나 행으로 조회하는 방법을 알아보는 것이 최우선입니다 

 

SELECT deptno, coount(*) AS CNT FROM EMP GROUP BY deptno;

 

 

하나의 행으로 출력하는 과정을 알아보도록 하겠습니다

아래의 코드를 분석해 봅시다

 

 

select deptno,
       case when deptno=10 then 1 else 0 end as deptno_10,
       case when deptno=20 then 1 else 0 end as deptno_20,
       case when deptno=30 then 1 else 0 end as deptno_30
  from emp
 order by 1

 

 

위의 코드로 아래와 같이 조회되는 것을 확인할 수 있습니다

위의 DEPTNO 컬럼은 가독성을 위한 값이니 참고하시기 바랍니다.

CASE WHEN THEN ELSE END를 활용해서 한 컬럼에 대해서 값을 출력합니다 

그 값은 간단합니다 DEPTNO가 10이면 1을 주고 나머지는 0을 부여하게 됩니다.

그리고 두 번째 컬럼은 DEPTNO가 20, 세번째 컬럼은 DEPTNO가 30으로

아래의 데이터가 조회됩니다  그러나 아직은 조금 미흡합니다

아직 행이 하나가 아니기 때문이지요

 

그럼 조금 수정해보겠습니다

 

통계쿼리3
case문으로 쿼리 조회

 

 

아래의 코드로 합계결과 값은 하나의 행으로 반환하였습니다

위의 결과 값에서 집계 함수 SUM을 사용하여 각 DEPTNO의 발생 횟수를 계산합니다

 

 

 select sum(case when deptno=10 then 1 else 0 end) as deptno_10,
       sum(case when deptno=20 then 1 else 0 end) as deptno_20,
       sum(case when deptno=30 then 1 else 0 end) as deptno_30
  from EMP;

 

아래와 같이 최종 결과가 출력됩니다 

 

 

통계쿼리4
최종 결과

 

 

하지만 아직 조금 부족합니다 그 이유는 한 테이블에서 CASE문을 전체를 적용해서 

시스템상 성능 부하가 올 수 있습니다 그리고 단순히 CASE 표현식을 합산하는 방법을 이기 때문입니다

지금은 데이터가 많이 없기 때문에 이 같은 방법을 해도 문제가 없지만 데이터가 몇만 건씩 넘어간다면

문제가 발생할 것입니다

다른 방법을 알아보도록 하겠습니다

 

 

select case when deptno=10 then empcount else null end as deptno_10,
  case when deptno=20 then empcount else null enD as deptno_20,
  case when deptno=30 then empcount else null end as deptno_30
from (
        select deptno, count(*) as empcount
          from emp
         group by deptno
     ) x;

 

 

아래의 코드를 자세히 보시면

처음에 COUNT 함수를 사용했던 것을 기억하실 것입니다

그 쿼리를 인라인 뷰로 사용하여 부서당 사원수를 생성합니다

그럼 그 결과 값은 아래와 같이 출력되는 것을 확인할 수 있습니다

 

이 방법은 인라인 뷰로 먼저 생성된 데이터가 3개의 행이므로

CASE문 실행할 경우 전체 행을 읽지 않고 3개의 행만으로

결과를 출력할 수 있습니다 

 

 

통계쿼리5
인라인 뷰 활용 예

 

그런 다음 MAX 함수로 열을 하나의 행으로 축소를 합니다

아래의 코드를 확인해 봅시다

 

select max(case when deptno=10 then empcount else null end) as new_deptno_10,
       max(case when deptno=20 then empcount else null end) as new_deptno_20,
       max(case when deptno=30 then empcount else null end) as new_deptno_30
from (
        select deptno, count(*) as empcount
          from emp
         group by deptno
     ) x;

 

결과 값은 아래와 같이 출력되는 것을 확인할 수 있습니다

 

 

통계쿼리6
성능강화 쿼리 적용 결과값

 

아래는 사례별로 통계 쿼리를 작성하는 것을 포스팅 연결하였습니다

그리고 마지막에는 통계쿼리 응용문제가 있으니

확인하셔서 공부하는데 도움이 되셨으면 합니다 감사합니다

 

 

2022.08.08 - [IT_Web/Oracle] - Oracle mybatis 활용 DECODE CASE GROUP BY ROLLUP 회원 통계 쿼리 월별 분기별 연도별

 

Oracle mybatis 활용 DECODE CASE GROUP BY ROLLUP 회원 통계 쿼리 월별 분기별 년도별

회원 월별 분기별 년도별 통계 쿼리 오늘은 회원별 통계 쿼리를 알아보도록 하겠습니다 mybatis를 활용해서 여러 조건문을 한번에 처리하는 쿼리를 작성하도록 하겠습니다 selectType을 프론트 단에

tantangerine.tistory.com

 

 

2022.08.09 - [IT_Web/Oracle] - oracle 회원 가입 증가 현황 DECODE 통계 쿼리 CASE, GROUP BY 활용

 

oracle 회원 가입 증가현황 DECODE 통계 쿼리 CASE, GROUP BY 활용

회원 가입 증가 현황 통계 쿼리 회원 가입 통계 쿼리를 만들어 보려고 합니다 저도 아직 익숙하지 않아서 조금 더 좋은 방법 있다면 댓글 남겨주세요~ 등록일(REG_DT)을 기준으로 신규회원 가입현

tantangerine.tistory.com

 

2022.08.10 - [IT_Web/Oracle] - oracle 상품 판매 통계 쿼리 union all case group by 활용

 

oracle 상품 판매 통계 쿼리 union all case group by 활용

상품 판매 통계 쿼리 오늘은 상품 판매 통계 쿼리를 만들어 보겠습니다 상품은 여러 종류가 있습니다 그것을 어떻게 구분할지는 스스로가 정해야겠지요 아래에서는 상품구분을 단순하게 알파

tantangerine.tistory.com

 


 

 


 

 

사례로 보는 통계 쿼리 문

* 아래의 문제는 쿼리문을 집중해서 보시고 이렇게도 쿼리를 작성할 수 있구나 하고

생각해보는 시간이 되었으면 합니다

 

 

 

 

HP라는 제품은 너무 많은 등급을 가지고 있어 제품별로 분석하기를 원하고, ‘LD’라는 제품은 등급의 수가 적고 중요하므로 등급별로 분석하기를 원하며, ‘PP’라는 제품은 다양한 등급을 가지고 있으나 ‘P530C’라는 등급은 전략적으로 관리하고자 하여 등급별로 분석하고, 나머지는 기타로 모아주기를 원한다고 생각하자

 

이런 경우에는 CASE와 GROUP BY를 활용하여 풀어보자

 

SELECT

  CASE

    WHEN 제품 = ‘HP’ THEN 제품

    WHEN 제품 = ‘LD’ THEN 등급 ,

    WHEN 등급 = ‘P530C’ THEN 등급

    ELSE ’기타‘

  END

  , SUM(총매출), SUM(총수량).....

FROM 매출테이블

WHERE 매출일자 LIKE ’9808%’

GROUP BY

  CASE

    WHEN 제품 = ‘HP’ THEN 제품

    WHEN 제품 = ‘LD’ THEN 등급 ,

    WHEN 등급 = ‘P530C’ THEN 등급

    ELSE ’기타‘

  END

 


 

SALE_HIST 테이블을 참조하여 ‘PENCIL’ 총판매금액과 01번 사업장의 총판매금액과

ERASER를 판매하고 있는 사업장의 총판매 금액을 출력하고 그 나머지를 기타로 묶어 총판매금액을 출력하시오

통계쿼리7
SALE_HIST 테이블 데이터 정보

 

SELECT

  CASE

    WHEN SALE_ITEM = 'PENCIL' THEN SALE_ITEM

    WHEN SALE_ITEM = 'ERASER' THEN SALE_SITE

    WHEN SALE_SITE = 01 THEN SALE_SITE

    ELSE '기타'

  END AS 특별관리항목

  , SUM(SALE_AMT) AS 총판매금액

FROM SALE_HIST

  GROUP BY

    CASE

      WHEN SALE_ITEM = 'PENCIL' THEN SALE_ITEM

      WHEN SALE_ITEM = 'ERASER' THEN SALE_SITE

      WHEN SALE_SITE = 01 THEN SALE_SITE

      ELSE '기타'

    END

 

통계쿼리8
상품별 총판매금액 통계쿼리

 

 


 

 

 

TEST100 테이블을 참조하여 C2에 해당하는 값의 비율을 구하여라

 

통계쿼리9
TEST100 테이블 데이터 정보

 

SELECT C1, C2

, ROUND(RATIO_TO_REPORT(C2) OVER(),2) * 100 AS PER

FROM TEST100

 

통계쿼리10
TEST100 상품별 비율

 


 

 

TEST35 테이블을 참조하여 KEY1은 부서라고 생각하고 KEY2의 A는 판매액 B는 매입액이라고 할 때

부서별 판매액과 매입액의 차에 대한 값(수익)을 구하고 차액의 비율을 구하시오

 

통계쿼리11
TEST35 테이블 데이터 정보

 

 

SELECT

  CASE WHEN KEY1 IS NOT NULL THEN KEY1 ELSE '합계' END AS KEY1

  ,  SUM(CASE WHEN KEY2 = 'A' THEN AMT ELSE 0 END) AS 판매액

  ,  SUM(CASE WHEN KEY2 = 'B' THEN AMT ELSE 0 END) AS 매입액

  ,  SUM(CASE WHEN KEY2 = 'A' THEN AMT ELSE -AMT END) AS 수익

  ,  ROUND(SUM(CASE WHEN KEY2='A' THEN AMT ELSE -AMT END)

                        / SUM(CASE WHEN KEY2='A' THEN AMT END)*100 ) AS PER

FROM (SELECT KEY1, KEY2, SUM(AMT) AMT

               FROM TEST35

               GROUP BY ROLLUP(KEY1),  KEY2

               )

GROUP BY KEY1;

 

 

통계쿼리12
부서 판매액 차액 비율

 


 

 

Temp를 이용한 문제를 풀어보자

Temp에 있는 직원들을 부서별 직급별로 SALARY 합을 보려 한다 이때 부서별로 직급들이 동일행에 나오도록

보여주고 부서별 전체 급여 그리고 PER컬럼의 전체 부서 급여 합계 비율을 구하여라

 

 

통계쿼리13
TEMP 테이블 데이터 정보

 

 

SELECT CASE WHEN DEPTNO IS NOT NULL THEN DEPTNO ELSE '합계' END DEPTNO

                 , SUM(A.부장) 부장, SUM(A.차장) 차장, SUM(A.과장) 과장

                 , SUM(A.대리) 대리, SUM(A.사원) 사원, SUM(A.수습) 수습, SUM(A.PER) 전체급여비율

                 , SUM(A.SAL) 부서별급여합계

FROM (SELECT DEPT_CODE DEPTNO, SALARY SAL

                               , SUM(CASE WHEN LEV = '부장' THEN SALARY ELSE 0 END) 부장

                               , SUM(CASE WHEN LEV = '차장' THEN SALARY ELSE 0 END) 차장

                               , SUM(CASE WHEN LEV = '과장' THEN SALARY ELSE 0 END) 과장

                               , SUM(CASE WHEN LEV = '대리' THEN SALARY ELSE 0 END) 대리

                               , SUM(CASE WHEN LEV = '사원' THEN SALARY ELSE 0 END) 사원

                               , SUM(CASE WHEN LEV = '수습' THEN SALARY ELSE 0 END) 수습

                               , ROUND(RATIO_TO_REPORT(SALARY) OVER (),2) * 100 AS PER

                               , SUM(SALARY)

              FROM TEMP

              GROUP BY DEPT_CODE, SALARY, LEV

              ) A

GROUP BY ROLLUP(DEPTNO)

ORDER BY DEPTNO

 

 

통계쿼리14
부서별 전체 급여 비율

 


 

 

TEST02 테이블의 자료를 이용해 최대 CRATE를 가지는 일자의 AMT와 최소 CREATE를 가지는 일자의 AMT를 읽어오는 문장을 출력하시오

 

통계쿼리15
TEST02 테이블 정보

 

 

SELECT distinct

  LAST_VALUE(AMT) OVER( ORDER BY CRATE

                                    ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) MAX_VAL

  , FIRST_VALUE(AMT) OVER( ORDER BY CRATE

                                       ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) MIN_VAL

FROM TEST02

 

 

통계쿼리16
최대 최소 금액출력

 

                    


 

 

이렇게 통계 쿼리를 작성법을 알아보았습니다

조금이나마 도움이 되었으며 합니다

저도 통계 쿼리를 접할 때 많이 힘들었던 적이 있어서 이렇게 준비하게 되었습니다

그럼 앞으로 공부할 날이 너무나도 많기에 힘내시고 

같이 파이팅합시다

 

그럼 다음 포스팅도 기대해주세요!

 

728x90
반응형
728x90
반응형

상품 판매 통계 쿼리

 

오늘은 상품 판매 통계 쿼리를 만들어 보겠습니다

상품은 여러 종류가 있습니다

그것을 어떻게 구분할지는 스스로가 정해야겠지요

 

아래에서는 상품구분을 단순하게 알파벳으로 표현하였습니다

그 알파벳으로 CASE문을 사용하여 상품명을 가져왔습니다

 

보통은 공통코드가 만들어져 조인을 걸어서 사용해야만 합니다

그래야 지만 상품이 늘어날 때마다 쿼리를 수정하지 않고 데이터를 추가하는 것만으로도

상품이 추가되어서 쿼리가 작동되는 범용성을 가져야 한다는 것이지요

 

그래서 솔직히 아래의 코드는 좋은 쿼리라고 할 수 없습니다

 

하지만 CASE문을 어떻게 써서 열 데이터를 행열로 만들어서 카운팅 하는 지만 신경 써서 보시길 바랍니다

 

<select id="selectProductSaleTotalIncrease" parameterType="Map" resultType="Map">
    SELECT
           DI_POM_SP.PRD_TYPE
          ,TO_CHAR(DI_POM_SP.PAY_CNT,'FM9999,999') AS PAY_CNT
          ,TO_CHAR(DI_POM_SP.PAY_AMT,'FM999,999,999,999') AS PAY_AMT
      FROM
           (SELECT
           		   /* 상품에 대한 구분(소분류, 중분류, 대분류등등) */
                   (CASE WHEN SP.PRD_DVCD = 'A' THEN '이용권'
                         WHEN SP.PRD_DVCD = 'B' THEN '상품 구매'
                         WHEN SP.PRD_DVCD = 'C' THEN '내부 서비스 수수료'
                         WHEN SP.PRD_DVCD = 'D' THEN '외부 서비스 수수료'
                         WHEN SP.PRD_DVCD = 'E' THEN '기타 수수료'
                         ELSE 'SKIP' END) AS PRD_TYPE
                   ,SUM(NVL(DI_POM.PAY_CNT,0)) AS PAY_CNT
                   ,SUM(NVL(DI_POM.PAY_AMT,0)) AS PAY_AMT
              FROM
                  (SELECT DI.YMD
                         ,DI.MMDD
                         ,NVL(POM.PRD_DVCD,'SKIP') AS PRD_DVCD
                         ,NVL(POM.PAY_CNT,0) AS PAY_CNT
                         ,NVL(POM.PAY_AMT,0) AS PAY_AMT
                     FROM
                          (SELECT TO_CHAR(TO_DATE(YMD),'YYYYMMDD') AS YMD, TO_CHAR(TO_DATE(YMD),'MM.DD') AS MMDD
                             FROM DAY_INFO
                             /* 기간을 정해서 판매량을 추출할 수 있다 */
                            WHERE TO_CHAR(TO_DATE(YMD),'YYYYMMDD') BETWEEN #{startDt} AND #{endDt}
                          ) DI
                         ,(SELECT
                                   OD.PRD_DVCD
                                  ,PM.PAY_DT
                                  ,SUM(1) AS PAY_CNT
                                  ,SUM(NVL(PM.PAY_AMT,0)) AS PAY_AMT
                             FROM PAY_MST PM
                                  INNER JOIN ODR_DTL OD ON OD.ODR_NUM = PM.ODR_NUM
                                  LEFT OUTER JOIN (SELECT A.ODR_NUM, A.ODR_DTL_SEQ, A.TRANS_SPEC_NUM, A.SEQ_NO, A.JRNR_NUM, A.SAP_NO
                                                     FROM STTL_JRNL_DTL A
                                                    WHERE A.STTL_DIV_CD = 'S1'
                                                      AND A.JRNR_NUM = (SELECT MAX(JRNR_NUM)
                                                                          FROM STTL_JRNL_DTL B
                                                                         WHERE B.STTL_DIV_CD = A.STTL_DIV_CD
                                                                           AND B.ODR_NUM = A.ODR_NUM
                                                                           AND B.ODR_DTL_SEQ  = A.ODR_DTL_SEQ
                                                                           AND B.SEQ_NO = A.SEQ_NO)
                                                   ) SJD ON SJD.ODR_NUM = OD.ODR_NUM AND SJD.ODR_DTL_SEQ = OD.ODR_DTL_SEQ
                              WHERE PM.PAY_DT BETWEEN #{startDt} AND #{endDt}
                              AND NVL(PM.PAY_AMT,0) > 0
                              GROUP BY PM.PAY_DT, OD.PRD_DVCD
							/* union all을 사용해서  group by (특정 상품에 대한 조인이 쉽지않을 때 사용)*/
                           UNION ALL
                           
                           SELECT
                                   'BA16' AS PRD_DVCD
                                  ,FINALBUYCFDT AS PAY_DT
                                  ,SUM(1) AS PAY_CNT
                                  ,SUM(NVL(TO_NUMBER(COVINSAMT),0)) AS PAY_AMT
                             FROM HSVC_EW_INSUPRD
                             WHERE FINALBUYCFDT BETWEEN #{startDt} AND #{endDt}
                             AND NVL(TO_NUMBER(COVINSAMT),0) > 0
                             GROUP BY FINALBUYCFDT
                           ) POM
                    WHERE DI.YMD = POM.PAY_DT(+)
                  ) DI_POM,
                  (SELECT PRD_DVCD
                    FROM SL_PRD) SP
             WHERE SP.PRD_DVCD = DI_POM.PRD_DVCD(+)
             GROUP BY SP.PRD_TYPE
             ORDER BY SP.PRD_TYPE
          ) DI_POM_SP
     WHERE A.PRD_TYPE != 'SKIP'
</select>

 

 

우선 상품이 판매된 날짜와 상품 구분 값으로 SUM을 하게 됩니다

 

또 한,

상품 금액의 총합계를 구하여  상품별 총판매금액을 구하게 됩니다

 

상품 총판매금액
이용권 10000
상품구매 20000
내부 서비스 수수료 30000

 

 

그럼 쿼리를 분석하고 곱씹어보는 것도 공부가 되겠지요

그럼 오늘도 즐거운 공부 하셨길 바랍니다

그럼 다음 포스팅도 기대해주세요

 

우리의 대모험이 끝나기까지 말이지요

728x90
반응형
728x90
반응형

 

회원 가입 증가 현황 통계 쿼리

 

 

회원 가입 통계 쿼리를 만들어 보려고 합니다

저도 아직 익숙하지 않아서 조금 더 좋은 방법 있다면

댓글 남겨주세요~

 

 

등록일(REG_DT)을 기준으로 신규회원 가입현황을 알아볼 수 있게 쿼리를 만들어 봅시다

밑의 쿼리는 금일 신규가입 현황을 볼 수 있는 쿼리입니다

param값을 todayoneDay를 받아서 처리하기 때문에

기간별 신규회원 증가 수를 확인할 수 있습니다

 

여기 가장 중요한 것은 서브 쿼리를 만들 때 등록일 기준으로 카운팅을 하고 등록일 시분초로 기준으로

데이터를 추출하여 GROUP BY를 해야 한다는 것입니다

 

그럼 아래의 쿼리를 천천히 봅시다~

 

SELECT 
    decode(MB_TPCD, '0010', '일반', '0020', '개인사업자', '0030',  '법인', '0040', '단체') as memberType,
    REG_CNT AS total
  FROM (
        SELECT 
            T1.MB_TPCD
            , NVL(REG_DT, #{today}) AS REG_DT
            , REG_CNT
          FROM (
                SELECT
                    MBM.Mb_TPCD
                    , A.REG_DT
                    , COUNT(A.REG_DT) AS REG_CNT
                  FROM (
                        SELECT 
                            /* 오늘날짜를 할당해서 기간동안의 증가 현황을 볼 수 있게 추출한다 */
                            TO_CHAR(REG_DT, 'YYYYMMDD') AS REG_DT
                            , MB_TPCD
                        FROM MB_MST
                        /* 오늘날짜를 할당해서 기간동안의 증가 현황을 볼 수 있게 추출한다 */
                        WHERE REG_DT BETWEEN TO_DATE(#{today} || '000000', 'YYYYMMDDHH24MISS') AND TO_DATE(#{oneDay} || '235959', 'YYYYMMDDHH24MISS')                                    
                        ) AS MBM,
                GROUP BY MBM.MB_TPCD, MBM.REG_DT
               ) AS T1
       ) AS VW1
 /* 금일이나 특정날을 지정해서 증가 현황을 확인할 수 있다 */
 WHERE VW1.REG_DT BETWEEN TO_DATE(#{today}) AND TO_DATE(#{oneDay})                                    
 ORDER BY MB_TPCD ASC

 

이렇게 신규 회원 증가 현황 쿼리를 알아보았습니다

금일도 괜찮고 기간별로도 조회할 수 있는 쿼리를 만들어 보았습니다

통계는 비즈니스적인 요소가 많이 필요하다고 생각합니다

 

회원 통계는 그렇게 많이 필요하지 않지만

어떤 상품에 대한 판매현황, 광고효과 분석 통계 쿼리를 만들기 위해서는

조금은 더 복잡한 쿼리가 되지 않을까 생각합니다

그럼 오늘은 여기까지 하고 다음 포스팅을 기대해주세요

 

언제나 우리의 대모험을 위해

오늘도 한 걸음씩 나아갑시다

 

그럼 파이팅!!

728x90
반응형
728x90
반응형

회원 월별 분기별 년도별 통계 쿼리

 

 

오늘은 회원별 통계 쿼리를 알아보도록 하겠습니다

mybatis를 활용해서 여러 조건문을 한번에 처리하는 쿼리를 작성하도록 하겠습니다

 

selectType을 프론트 단에서 month, quarter, recentYear의 값들을 받아옵니다

그리고 회원 테이블인 MB_MST에서 등록일자와 회원구분에 대한 값을 서브쿼리로 작성합니다

 

회원 구분별로 GROUP BY ROLLUP을 하여 진행한다는 것이 핵심입니다

 

GROUP BY ROLLUP 밑에 포스팅도 걸어 놓았습니다

 

2020.03.08 - [IT_Web/Oracle] - Oracle ROLLUP, CUBE, GROUPING(), GROUPING SETS(), GROUP_ID() 함수 개념

 

Oracle ROLLUP, CUBE, GROUPING(), GROUPING SETS(), GROUP_ID() 함수 개념

ROLLUP ROLLUP은 SELECT한 컬럼들을 GROUP BY 절과 함께 사용하게 되면 소계를 구할 수 있다 그 과정에는 CARTESIAN PRODUCT를 이용한 결과물들이란 것을 알아두자 또 한 ROLLUP의 인수는 계층 구조이므로 인수

tantangerine.tistory.com

 

 

그럼 실제 코드 사례를 한번 보도록 하겠습니다

자세히 보시면 등록일로 하여 컨트롤 하는 것을 보실 수 있습니다

 

 

<select id="selectNewMemberDetail" parameterType="Map" resultType="Map">
    SELECT 
        /*  DECODE를 활용해서 회원구분별로 누적 합계를 추출한다 */
        decode(CD_ID, '0010', 'normalSum', '0020', 'individualSum', '0030',  'organizationSum', '0040', 'affilicorpSum','membertypeSum') as memberType
    <choose>
        <when test="selectType == 'month'"> /*  DECODE 함수를 활용해서 월별로 필드를 추출한다 */
            , count(decode(to_char(reg_dt, 'MM'),'01',0)) "M01"
            , count(decode(to_char(reg_dt, 'MM'),'02',0)) "M02"
            , count(decode(to_char(reg_dt, 'MM'),'03',0)) "M03"
            , count(decode(to_char(reg_dt, 'MM'),'04',0)) "M04"
            , count(decode(to_char(reg_dt, 'MM'),'05',0)) "M05"
            , count(decode(to_char(reg_dt, 'MM'),'06',0)) "M06"
            , count(decode(to_char(reg_dt, 'MM'),'07',0)) "M07"
            , count(decode(to_char(reg_dt, 'MM'),'08',0)) "M08"
            , count(decode(to_char(reg_dt, 'MM'),'09',0)) "M09"
            , count(decode(to_char(reg_dt, 'MM'),'10',0)) "M10"
            , count(decode(to_char(reg_dt, 'MM'),'11',0)) "M11"
            , count(decode(to_char(reg_dt, 'MM'),'12',0)) "M12" 
        </when>
        <when test="selectType == 'quarter'"> /*  DECODE 함수를 활용해서 분기별로 필드를 추출한다 */
            , count(decode(to_char(reg_dt, 'MM'),'01',0)) + count(decode(to_char(reg_dt, 'MM'),'02',0)) + count(decode(to_char(reg_dt, 'MM'),'03',0)) "Q1"
            , count(decode(to_char(reg_dt, 'MM'),'04',0)) + count(decode(to_char(reg_dt, 'MM'),'05',0)) + count(decode(to_char(reg_dt, 'MM'),'06',0)) "Q2"
            , count(decode(to_char(reg_dt, 'MM'),'07',0)) + count(decode(to_char(reg_dt, 'MM'),'08',0)) + count(decode(to_char(reg_dt, 'MM'),'09',0)) "Q3"
            , count(decode(to_char(reg_dt, 'MM'),'10',0)) + count(decode(to_char(reg_dt, 'MM'),'11',0)) + count(decode(to_char(reg_dt, 'MM'),'12',0)) "Q4"
        </when>
        <when test="selectType == 'recentYear'"> /*  DECODE 함수를 활용해서 년별로 필드를 추출한다 */
            , count(decode(to_char(reg_dt, 'YYYY'),to_char(to_char(sysdate,'YYYY')-3),0)) "currentYear03"
            , count(decode(to_char(reg_dt, 'YYYY'),to_char(to_char(sysdate,'YYYY')-2),0)) "currentYear02"
            , count(decode(to_char(reg_dt, 'YYYY'),to_char(to_char(sysdate,'YYYY')-1),0)) "currentYear01"
            , count(decode(to_char(reg_dt, 'YYYY'),to_char(sysdate,'YYYY'),0)) "currentYear"
        </when>
   </choose> 
   /* DECODE 함수를 활용해서 전체합계 추출한다 */
   , count(mb_tpcd) total 
    FROM  
    (
        SELECT  
            reg_dt,
            MB_TPCD
        FROM MB_MST
        WHERE SUBSTR(reg_dt,1,4)  = #{selectYear}                
    ) MBM
    GROUP BY ROLLUP(MB_TPCD) /* 합계 및 소계를 나타낼수 있는 함수 */
</select>

 

 

이렇게 회원 월별 분기별 년도별을 알아보았습니다

조금은 어려울 수 있지만

작은 쿼리를 만들어서 테이블을 생성해서 하나씩 만들어보는 것도 좋을 듯합니다

그럼 오늘도 공부 열심히 하시고!

다음 포스팅도 많은 기대부탁드립니다~

 

728x90
반응형
728x90
반응형

오라클에서는 트리구조가 아주 중요합니다

level ~ start with ~ connect by prior ~를 많이 사용합니다

그러나 오라클을 처음 접하는 사람은 이 구문을 한 번에 이해하기가 쉽지 않습니다

그래서 한번 이 구문의 예문을 통해 같이 알아보고자 합니다

 

 

아래와 같이 테이블이 존재합니다

 

SELECT DEPTNO, ENAME FROM EMP

 

 

 

가공 전 데이터

 

위의 데이터로 아래와 같이 출력하고자 합니다

어떤 사람은 case문으로 하면 안 되지 않을까 하고 의문을 갖는 사람도 있을 거라 생각합니다

당연히 가능합니다

하지만 case문은 deptno의 값이 쿼리에 값 조건문이 들어가야 합니다

이러한 경우는 deptno가 증가할 경우에 대해 전혀 대비가 되지 않은 쿼리라고 할 수 있습니다

프로젝트를 진행하다 보면 case문을 자주 사용하게 되는데

그 함정에 빠져 큰 그림을 보지 못하고 당장 앞만 보게 되는 경우가 있습니다

그래서 대도록이면 테이블의 데이터 값이 쿼리문에는 없는 것이 범용성이 좋은 쿼리라고 할 수 있습니다

 

그리고 이러한 재밌는 쿼리를 보면

여러 가지를 생각나게 합니다

밑에 deptno별로 모든 직원을 조회해야 한다면

밑에 처럼 안 하면 java에서 map형 list로 담아서 진행해야 합니다

하지만 아래처럼 한다면 list형 map으로 간단히 담아질 것으로 보입니다

하지만 그렇게 되면 자바에서 또다시 다른 작업을 진행해야 하겠지요

아무튼!!

계속해서 보도록 하겠습니다

 

 

 

최종 데이터

 

 

 

아래의 코드로 출력이 가능합니다

처음 접하면 너무 복잡하고 어려워 보이겠지만

같이 천천히 봅시다

 

 

 

 select deptno,
         ltrim(sys_connect_by_path(ename,','),',') emps
    from (
  select deptno,
         ename,
         row_number() over
                  (partition by deptno order by empno) rn,
         count(*) over
                  (partition by deptno) cnt
   from emp
        )
  where level = cnt
  start with rn = 1
 connect by prior deptno = deptno and prior rn = rn-1;

 

 

 

 

반응형

 

 

우선 서브 쿼리를 단독 실행해서 사원에 대한 소속 부서, 이름 empno 오름차순 정렬로 결과가 노출되고 있습니다

순위에 해당 하는 RN는 트리를 이동하는 목적입니다.

 


 

start with

start with rn = 1이라는 것은 rn이 1일될때마다 새로운 트리를 만들게 됩니다

 

connect by prior

connect by prior deptno = deptno는 자기 자신을 상위 레벨로 지정을 하고

한 조건을 더 붙여서  rn = rn-1로 합니다

이 의미는 자기 이전행을 상위 레벨로 하겠다 라는 것입니다

이렇게 되면 부서별 마지막 사람이 최하위 레벨이 되며

sys_connect_by_path를 사용하여 모든 행의 이름을 한 칼럼에 노출시킬 수 있습니다

 

sys_connect_by_path

데이터를 읽어오는 그 행의 모든 상위 레벨을 나타낼 수 있게 도와주는 함수입니다

읽어오는 그 행이 최상위라면 자기 행의 데이터만 가져오게 될 것이며,

최하위라면 모든 레벨의 데이터를 가져오게 될 것입니다

 

level

connect by prior를 사용하게 되면 트리구조로 그 행의 트리 레벨을 표현할 수 있습니다

그래서 위와 같이  where level = cnt라는 것은

총인원수를 와 같은 레벨만 보여달라는 것으로

deptno 10은 cnt가 3입니다

그래서 아래와 같이 10은 cnt가 3이며 level도 3인 행만 보여달라는 where 절이였습니다

 

 


 

 

where 절인 level 조건부를 제거하여 노출한 데이터

 

 

 

  select deptno,
         ename,
         row_number() over
                  (partition by deptno order by empno) rn,
         count(*) over
                  (partition by deptno) cnt
   from emp

 

 

 

위의 서브 쿼리를 출력할 경우 아래와 같이 노출됩니다

 

서브쿼리만 출력한 데이터

 

 

이렇게 트리구조에 대해서 알아보았습니다

확실히 트리구조는 무척이나 어렵습니다

처음 접하신 분은 이해가 어려울 수도 있을 것 같습니다

하지만 각각 쓰는 구문들이 어떤 역할을 하는지만

정확하게 알고 있다면 다른 사람이 작성한 코드를 분석하는데

많은 도움이 될 것이라 생각합니다

 

힘내시고 화이팅하세요!!

아직 모험은 끝나지않았습니다!!

파이팅!!

 

728x90
반응형
728x90
반응형

프로젝트를 진행하다 보면

지역별, 지점별 기준으로 업데이트하는 경우가 많습니다

그렇때마다 지점별, 지역별 쿼리를 만들어서 inset를 하는 경우가 있습니다

 

 

DEPT 테이블 행의 값을

EAST, WEST, MID의 세 테이블에 INSERT 하고 

테이블 구조는 DEPT와 같다고 가정합시다

 

 

 

한번에 여러 테이블 데이터 삽입

 

아래와 같은 구문을 사용하여 INSERT 하게되면

한번의 결과값으로 동시에 여러개의 테이블에 INSERT합니다

 

 

INSERT ALL
INTO EAST VALUES (DEPTNO, DNAME, LOC)
INTO WEST VALUES (DEPTNO, DNAME, LOC)
INTO MID VALUES (DEPTNO, DNAME, LOC)
SELECT (DEPTNO, DNAME, LOC) FROM DEPT WHERE DEPTNO = 20;

 

 

하지만 이 방법은 어떠한 조건없이

동일하게 같은 값들이 INSERT한다는 점으로

가용성이 떨어지는 면이 있습니다

 

 

한번에 여러 테이블 조건부 인설트 하기

 

다음 방법을 사용하면 같은 행의 값으로

둘 이상의 테이블에서 삽입할 수 있습니다

 

 

그럼 코드를 작성해봅시다

아래와 같이 코드를 작성하면

한 행으로 여러 테이블에 insert 할 수 있습니다

 

 

INSERT ALL
  WHEN LOC IN ('NEW YORK', 'BOSTON') THEN 
  	INTO EAST (DEPTNO, DNAME, LOC) VALUES (DEPTNO, DNAME, LOC)
  WHEN LOC = 'CHICAGO' THEN
    INTO MID (DEPTNO, DNAME, LOC) VALUES (DEPTNO, DNAME, LOC)
  ELSE
    INTO WEST (DEPTNO, DNAME, LOC) VALUES (DEPTNO, DNAME, LOC)
SELECT DEPT, DNAME, LOC
FROM DEPT

 

 

INSERT ALL과 INSERT FIRST 제대로 알고 사용하기

위와 같은 경우는 INSERT ALL 또는 INSERT FIRST 문을 사용할 수 있습니다

두 개의 구문은 같은 결과를 생성하지만

차이점은 존재합니다

 

INSERT FIRST는 조건이 참이 되는 즉시 WHEN-THEN-ELSE에서 빠져나오는

반면 INSERT ALL은 이전 테스트가 참으로 평가되더라도  

모든 조건을 평가합니다

 

따라서 INSERT ALL을 사용하여 같은 행을 둘 이상의 테이블에 삽입할 수 있습니다

그것이 싫다면 FIRST를 사용해야 합니다

 

mybatis를 사용하여 동적 쿼리와 같이 사용한다면

가용성은 더욱 높아질 거라 생각합니다

 

프로젝트를 진행하다보면 여러 데이터를 핸들링하게 되며

어떤 쿼리가 있는지 알아야지만

적재적소에 사용하여 시간을 절약하며

코드도 간결해질수 있을거라 생각합니다

 

오늘도 코딩으로 한걸음 나아갑시다

모두 화이팅하시고 힘내세요!!

아직 대모험은 끝나지 않았으니까요!!

 

화이팅!!

 

 

 

 

 

 

728x90
반응형
728x90
반응형

카티시안(Cartesian) 곱을 이용한 조인

어떤 제품의 월별로 매출집계를 보고자 한다면 매출이 발생하지 않은 달에도 매출이 없다는 다는 것을 알기 위해 0값을 가진 로우를 출력하는 것이 좋을 것이다 이와 같이 발생한 값의 유무에 관계 없이 고정된 양식에 로우를 출력하고자 한다면 생각보다 쉽지는 않다

 

SELECT y.계정명,

  sum(decode(x.공정, ‘원사’, 금액)) 원사,

  sum(decode(x.공정, ‘제작’, 금액)) 재직,

  sum(decode(x.공정, ‘기모’, 금액)) 기모,

  sum(decode(x.공정, ‘염색’, 금액)) 염색,

  sum(decode(x.공정, ‘가공’, 금액)) 가공

FROM

  (SELECT substr(계정과목, 1, 2) 항목, 공정, sum(금액) 금액

    FROM 전표테이블

    WHERE 사업장 = ‘울산공장’

   and 전표일자 like :작업월||‘%’

   and 계정과목 between ‘1234’ and ‘6543’

   GROUP BY substr(계정과목, 1, 2), 공정) x

   , 계정테이블 y

WHERE y.계정과목 between ‘1200’ and ‘6500’

and y.항목분류 = ‘1’

and x.항목(+) = substr(y.계정과목, 1, 2)

GROUP BY y.계정명;

 

 

테이블에 항목을 나타낼수 있는 계정과목 같은 테이블이 개별적으로 존재한다면 그 테이블을 이용하여 카티시안 조인과 outer 조인을 활용하여 정보가공은 전표테이블로 가공을 하고 계정명은 과목만 나타나있는 계정테이블을 활용하여 select를 한다

항목 분류가 중요한 이유는 집합은 테이터 발생 상황에 따라 값이 존재하지 않는 계정과목이 생길 수가 있다 그래서 계정테이블이 출력한 로우 단위인 계정들만 추출하여 GROUP BY한 집합을 OUTER 조인을 하기 위함이다

하지만 항목분류번호는 없을 수 있다 그 경우는 임시테이블을 만들어서 항목별로 분류코드를 부여하여 활용하면 된다

 

728x90
반응형
728x90
반응형

카테시안(Cartesian) 곱을 이용한 조인 – 관계가 없는 테이블간의 조인

커서를 선언하여 오픈한 후 루프 내에서 반복하여 패치를 하면서 복잡한 처리를 할 때 여러개의 상위테이블 정보를 참조해야 원하는 가공을 처리할 수 있는 경우가 있다고 하자 그렇게 처리되고 가공을 하고 상수값을 결합하여 실행 한 수 그 결과를 애플리케이션에 보내주는 작업들을 진행한다 이러한 데이터 베이스 호출(DBMS CALL)은 시스템 오버헤드의 주범이라고 할 수 있다

반복 수행되는 구문 내에 여러개의 SQL이 나열되어 있는 것은 가능한 피하는 것이 좋다

 

SELECT ename, job, sal, y.loc

FROM EMP x, DEPT y

WHERE x.empno=:emp_no

AND x.sal=:sal_no

AND y.dname(+) = :dname_no||substr(x.empno,0,0)

 

조인할 때 어느 한 집합만 공집합이 되더라도 전체가 공집합이 되어버리므로 다른 성공한 집합까지 실패하게 된다

그래서 y.dame(+) = :dname_no만 작성하게되면 해당하는 dame가 null이거나 만족하는 값이 없으면 오류가 난다

위와 같은 방법으로 조인하면 DEPTNO에 해당하는 dame가 null이거나 만족하는 값이 없더라도 SUBSTR의 연산값으로 NULL이 출력된다

OUTER-JOIN은 반드시 어떤 집합과 연결을 할 때만 의미가 있다

728x90
반응형
728x90
반응형

날짜를 연산 하는 함수를 알아보겠습니다

오라클은 더 많은 글들이 있으니 포스팅으로 여러 함수와 쿼리들을 확인해보세요

먼가 이상한것이 있거나 의문점이있으면 댓글 남겨주세요

읽어보고 답변 남겨 놓겠습니다

 

그럼 본론으로 들어가겠습니다

 

 

 

 

날짜 계산 (연산자)

SELECT

  SYSDATE + 100, --현재날짜 +100일

  SYSDATE - 100, --현재날짜 -100일

  SYSTIMESTAMP + 100, --현재날짜 +100일

  SYSTIMESTAMP - 100 --현재날짜 -100일

FROM DUAL

 

 

위와같이 + , - 연산자를 활용하여 간단한 날짜 계산이 가능합니다.

 

 

 

날짜 계산 (함수)

SELECT

  ADD_MONTHS(SYSDATE,6),     --현재시간 + 6개월 뒤

  LAST_DAY(SYSDATE),             --해당월 마지막 일자 계산

  NEXT_DAY(SYSDATE,'일요일'),  --다음 주 일요일 계산

  MONTHS_BETWEEN(SYSDATE, SYSDATE-100)      --DATE1과 DATE2의 개월 수 반환

FROM DUAL

 

 

ADD_MONTHS(date, integer) 

 - ADD_MONTHS함수는 매개변수 DATE에 매개변수 INTEGER만큼의 월을 더한 날짜를 리턴합니다.

 

LAST_DATE(date)

 - LASTE_DAY함수는 현재 월의 마지막 일자를 리턴합니다.

 

NEXT_DAY(date, char)

 - NEXT_DAY함수는 매개변수 date의 다음 주 char(요일)의 날짜를 리턴합니다.

 

MONTHS_BETWEEN(date1, date2)

 - MONTHS_BETWEEN함수는 매개변수 date1과 date2 사이의 개월수를 리턴합니다.

 

 

 

다양한 날짜 계산 EXAMPLE

SELECT

  TO_CHAR(SYSDATE ,'yyyy/mm/dd'), --오늘 날짜

 

  TO_CHAR(SYSDATE + 1 ,'yyyy/mm/dd'), --내일 날짜

 

  TO_CHAR(SYSDATE -1 ,'yyyy/mm/dd'), --어제 날짜

 

  TO_CHAR(TRUNC(SYSDATE,'dd') ,'yyyy/mm/dd hh24:mi:ss'), -- 오늘 정각 날짜

 

  TO_CHAR(TRUNC(SYSDATE,'dd') + 1,'yyyy/mm/dd hh24:mi:ss'), -- 내일 정각 날짜

 

  TO_CHAR(SYSDATE + 1/24/60/60 ,'yyyy/mm/dd hh24:mi:ss'), -- 1초 뒤 시간

 

  TO_CHAR(SYSDATE + 1/24/60 ,'yyyy/mm/dd hh24:mi:ss'), -- 1분 뒤 시간

 

  TO_CHAR(SYSDATE + 1/24 ,'yyyy/mm/dd hh24:mi:ss'), -- 1일 뒤 시간

 

  TO_CHAR(TRUNC(SYSDATE,'mm') ,'yyyy/mm/dd'), --이번 달 시작날짜

 

  TO_CHAR(LAST_DAY(SYSDATE) ,'yyyy/mm/dd'), --이번 달 마지막 날

 

  TO_CHAR(TRUNC(ADD_MONTHS(SYSDATE, + 1),'mm') ,'yyyy/mm/dd'), --다음 달 시작날짜

 

  TO_CHAR(ADD_MONTHS(SYSDATE, 1) ,'yyyy/mm/dd hh24:mi:ss'), -- 다음달 오늘 날자

 

  TO_CHAR(TRUNC(SYSDATE, 'yyyy') ,'yyyy/mm/dd'), --올해 시작 일

 

  TO_CHAR(TRUNC(ADD_MONTHS(SYSDATE, -12), 'dd'),'yyyy/mm/dd'), --작년 현재 일

 

  TO_DATE(TO_CHAR(SYSDATE, 'YYYYMMDD')) - TO_DATE('19930315'), -- 두 날짜 사이 일수 계산

 

  MONTHS_BETWEEN(SYSDATE, '19930315'), -- 두 날짜 사이의 월수 계산

 

  TRUNC(MONTHS_BETWEEN(SYSDATE, '19930315')/12,0) --두 날짜 사이의 년수 계산

 

FROM DUAL;

 

 

 

날짜 연산 초단위

  TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS')

  * SYSDATE -1 -> 1일전

  * SYSDATE -1 / (60*60*24) -> 1초전

  * SYSDATE -2 / (60*60*24) -> 2초전

  * SYSDATE -1 / (60*24) -> 1분전

  * SYSDATE -2 / (60*24) -> 2분전

  * SYSDATE -1 / (24) -> 1시간전

  * SYSDATE -2 / (24) -> 2시간전

 

 

1초전도 나누기를 활용하여 계산이 가능합니다

한번 신청하고 후 1시간이 지나지 않았다면 다시 신청 못하게 하는 로직을 만들수 있겠죠?

 

 

데이터에서 날짜를 컨트롤하는 경우는 무척많아서 꼭알아두어야 합니다

TO_DATE에는 문자열이 오면 날짜로 인식이 가능하다고 생각하시고!

언제나 외우려고 하지말고 항상 찾아보면서 몸으로 익히실려는 노력이 더 중요합니다!!

 

절대로 스트레스는 노노!!

728x90
반응형
728x90
반응형

카테시안(Cartesian) 곱을 이용한 조인 – 첨자 LOOP형 처리

이 사례는 대부분의 회사에서 발생할 수 있는 매출과 관련한 처리의 예이다

회사는 고객에게는 상품과 용역을 제공함으로써 매출이 발생한다 특히 이회사는 서비스 용역을 제공하기 전에 대부분의 금액을 고객으로부터 선수로 받는다 회계적인 입장에서 볼 때 미리 받은 대금은 아직 서비스 용역을 제공하지 않은 상태에서 발생한 금액이므로모두 매출로 인정할수 없고 선수금 계정으로 처리해야한다 아래와 같다

 

일련번호

발생일

시작일

종료일

월매출

 

1101

19970501

19970520

19970925

5,000,000

 

1102

19970601

19970615

19970805

3,800,000

 

1103

19970701

19970712

19971020

6,780,000

 

1104

19970901

19970507

19970718

2,890,000

 

 

위의 테이블을 참조하여 새로운 정보를 출력하는데 그 테이블이 5개의 로우로 분활되어야하며 각 로우의 적용기간에 대한 일수 와 해당 금액을 산정하여 ‘월별매출’ 테이블에 저장되어야 한다.

 

먼저 테이블을 생성하면서 기존 컬럼형 테이블을 로우 행으로 만들 때 사용할 넘버가 해당된다

 

CREATE TABLE COPY_T(NO, NO2) AS

 

SELECT ROWNUM, SUBSTR(TO_CHAR(ROWNUM, '09'), 2, 2)

FROM TEST99

WHERE ROWNUM <=31;

 

위와 같이 만들면 NO, NO2의 두 개의 컬럼과 31개의 로우 수를 가지는 테이블이 된다

 

윈도우 함수를 사용하면 쉽게 할 수 있을 것 같다 한번 확인해 보자 답은 다음에~

CREATE TABLE PAYMENT (

SERIAL NUMBER(4),

OCRNC_DATE NUMBER(8),

STRT_D NUMBER(8),

END_D NUMBER(8),

FRRGT_STMD NUMBER(38)

);

INSERT ALL

INTO PAYMENT

VALUES ( 1101, 19970501, 19970520, 19970925, 5000000 )

INTO PAYMENT

VALUES ( 1102, 19970601, 19970615, 19970805, 3800000 )

INTO PAYMENT

VALUES ( 1103, 19970701, 19970712, 19971020, 6780000 )

INTO PAYMENT

VALUES ( 1104, 19970501, 19970507, 19970718, 2890000 );

 

 

SELECT

  SERIAL

  , substr(OCRNC_DATE, 1, 4) || y.no2 AS 적용월

  , ROUND((CASE y.NO2 WHEN SUBSTR(END_D, 5, 2) THEN SUBSTR(END_D, 7, 2)

                                                 ELSE TO_CHAR(LAST_DAY(TO_DATE(END_D,'yyyymmdd')),'dd' )

                       END

     -

                     (CASE y.NO2 WHEN substr(STRT_D, 5, 2) THEN SUBSTR(STRT_D,7 , 2) ELSE '01' END )+1)

    *

                     (FRRGT_STMD/(TO_DATE(END_D,'yyyymmdd') - TO_DATE(STRT_D,'yyyymmdd')+1))) AS 월별매출액

FROM PAYMENT x, COPY_T y

WHERE Y.NO BETWEEN SUBSTR(STRT_D, 5, 2) AND SUBSTR(END_D, 5, 2)

AND x.OCRNC_DATE LIKE x.OCRNC_DATE ||'%'

 

SERIAL 적용월   월별매출액

1101   199705   426357
1101   199706   1162791
1101   199707   1162791
1101   199708   1162791
1101   199709   968992
1102   199706   1242308
1102   199707   2265385
1102   199708   365385
1103   199707   1342574
1103   199708   2080990
1103   199709   2080990
1103   199710   1342574
1104   199705   989726
1104   199706   1227260
1104   199707   712603

 

카테시안 곱으로 조인을 할때에는 ROWNUM을 활용하여 현재의 데이터와 조인을 하여 사용해야 한다

지금 현재의 데이터는 일련번호 별로 05월부터 09월 까지이다 그렇기 때문에 01부터 31까지의 로우를 사용해도 추후에는  조건절에서 범위를 정하여 자기가 필요한 05부터 09월의 데이터를 추출하는 방식이다

 

데이터는 CASE문으로 종료일에 해당하는 09월에는 25일 (THEN SUBSTR(END_D, 7, 2))을 가져오고

나머지는 그 달의 마지막날TO_CHAR(LAST_DAY(TO_DATE(END_D,'yyyymmdd')), 'dd' )을 가져오게된다

 

시작일에 해당하는 05월이 나오면 20일(SUBSTR(STRT_D,7 , 2))을 가져오고 나머지는 01로 치환한다

 

이렇게 되면 빠른 숫자인  05의 ROWNUM으로 인해

종요일은 05가 아니므로 TO_CHAR(LAST_DAY(TO_DATE(END_D,'yyyymmdd')), 'dd' ) 로 인해 

30일이 치환대어 뺄셈이 시작된다

 

시작일 05월에는 20일

그래서 결과 값은 10 이때 날자값에 +1를 잊지말자

그 10일로 일할매출액을 구하는 식이

(FRRGT_STMD/(TO_DATE(END_D,'yyyymmdd') - TO_DATE(STRT_D,'yyyymmdd')+1))

위와 같으며 TO_DATE을 사용하여 종요일과 시작일의 일수를 구하여 나누어 일할매출액을 구한다

 

그렇게 월별로 매출액을 구할수 있게된다

728x90
반응형
728x90
반응형

이번 카테시안 조인을 활용할 사례는 레코드를 생성시키는 것으로

계약번호에 대해 고객이 해약을 하여 정산을 거쳐 고객에게 지불할 보증금반환금과 청구해야할 위약금, 기기철거비용이 하나의 로우로 정보를 출력하고자 한다

 

먼저 테이블을 생성하면서 기존 컬럼형 테이블을 로우 행으로 만들 때 사용할 넘버가 해당된다

 

CREATE TABLE COPY_T(NO, NO2) AS

 

SELECT ROWNUM, SUBSTR(TO_CHAR(ROWNUM, '09'),2,2)

FROM TEST99

WHERE ROWNUM <=31;

 

위와 같이 만들면 NO, NO2의 두 개의 컬럼과 31개의 로우 수를 가지는 테이블이 된다

 

 

INSET into 전표테이블 (생성일자, ..., ..., 계정과목, 금액, ... )

SELECT to_char(sysdate, ‘yyyymmdd’),

 

          CASE y.no WHEN 1 THEN ‘1234’

                        WHEN 2 THEN ‘5678’

                        WHEN 3 THEN ‘9876’

           END,

          CASE y.no WHEN 1 THEN 보증금반환금

                        WHEN 2 THEN 위약금

                        WHEN 3 THEN 기기철거비

FROM 계약정산테이블 x, COPY_T y

WHERE x.해약일 = :input_date            <- PL/SQL

AND y.no in ( CASE 보증금반환금 WHEN 0 THEN null ELSE 1 END,

                   CASE 위약금 WHEN 0 THEN null ELSE 2 END,

                   CASE 기기철거비 WHEN 0 THEN null ELSE 3 END )

 

<결과>

2019-09-23 1234 보증금 반환금

2019-09-23 5678 위약금

2019-09-23 9876 기기철거비

 

카테시안으로 조인한 다음 조건문을 주고 그에 대해 결합을 한다

 

일반적인 '='이퀴조인이 아닌 카테시안 곱이 사용하과 CASE문을 활용하여 조인을 한 형태이다

이렇게 최대한의 로우 수를 줄여서 조인을 할 수 있는 방법을 더 생각해 보아야겠다

 

728x90
반응형
728x90
반응형

조인을 활용한 데이터 연결

 

우리는 기본키 간의 관계 테이블이면 거의 대부분의 사람들은 기본키를 사용하여 두 개의 관계를 ’ = ‘ 성립시켜 조인을 한다

 

이 때 M : 1 관계나 1 : M라면 상관없이 조인해서 정보를 출력해도 무관하다 하지만 우리는 최대한의 조인을 할 때 로우 수를 줄여주는 것이 좋다 M : M 관계라면 더 더욱이 그렇게 해야 할 것이다 그것은 이퀴조인으로 가능하다

 

’ = ’의 조인이 아니라 >= , <= 로 조인하여 1 : M으로 만들어 조인하는 것이다

기본키 만이 조인할 수 있는 것이 아니라 기본키가 외부키에 연결이 된다하더라도

테이블의 엔터티의 관계를 확인하여 이퀴조인이 가능하다면 되도록 이것을 사용하는 것을 권고한다

 

 

 

 

 

 

 

728x90
반응형
728x90
반응형

Temp를 이용한 문제를 풀어보자

Temp에 있는 직원들을 부서별 직급별로 SALARY 을 보려한다 이때 부서별로 직급들이 동일행에 나오도록

보여주고 부서별 전체급여 그리고 PER컬럼의 전체 부서 급여합계 비율을 구하여라

 

SELECT CASE WHEN DEPTNO IS NOT NULL THEN DEPTNO ELSE '합계' END DEPTNO

, SUM(A.부장) 부장, SUM(A.차장) 차장, SUM(A.과장) 과장

, SUM(A.대리) 대리, SUM(A.사원) 사원, SUM(A.수습) 수습, SUM(A.PER) 전체급여비율

, SUM(A.SAL) 부서별급여합계

FROM (SELECT DEPT_CODE DEPTNO, SALARY SAL

                               , SUM(CASE WHEN LEV = '부장' THEN SALARY ELSE 0 END) 부장

                               , SUM(CASE WHEN LEV = '차장' THEN SALARY ELSE 0 END) 차장

                               , SUM(CASE WHEN LEV = '과장' THEN SALARY ELSE 0 END) 과장

                               , SUM(CASE WHEN LEV = '대리' THEN SALARY ELSE 0 END) 대리

                               , SUM(CASE WHEN LEV = '사원' THEN SALARY ELSE 0 END) 사원

                               , SUM(CASE WHEN LEV = '수습' THEN SALARY ELSE 0 END) 수습

                               , ROUND(RATIO_TO_REPORT(SALARY) OVER (),2) * 100 AS PER

                               , SUM(SALARY)

             FROM TEMP

             GROUP BY DEPT_CODE, SALARY, LEV

              ) A

GROUP BY ROLLUP(DEPTNO)

ORDER BY DEPTNO

 

-->  컬럼 합계를 하기위해서는 ROLLUP()필요하다

그리고 select 문에 DEPTNO별로 소계를 한다면 DEPTNO가 NULL이 되어서 CASE문에 합계라는 문자를 줄 수 있다  

밑의 2개의 구문으로 컬럼의 합계를 표현할 수 있다

 

SELECT CASE WHEN DEPTNO IS NOT NULL THEN DEPTNO ELSE '합계' END DEPTNO

GROUP BY ROLLUP(DEPTNO)

 

 

TEST02 테이블의 자료를 이용해 최대 CRATE를 가지는 일자의 AMT와 최소 CREATE를 가지는 일자의 AMT를 읽어오는 문장을 출력하시오

 

SELECT distinct LAST_VALUE(AMT) OVER ( ORDER BY CRATE

                                                                                        ROWS BETWEEN UNBOUNDED PRECEDING

                                                                                        AND UNBOUNDED FOLLOWING) MAX_VAL,

FIRST_VALUE(AMT) OVER( ORDER BY CRATE

                                                      ROWS BETWEEN UNBOUNDED PRECEDING

                                                      AND UNBOUNDED FOLLOWING) MIN_VAL

FROM TEST02

728x90
반응형
728x90
반응형

CASE, GROUP BY 활용

 

'HP'라는 제품은 너무 많은 등급을 가지고 있어 제품별로 분석하기를 원하고,

'LD'라는 제품은 등급의 수가 적고 중요하므로 등급별로 분석하기를 원하며,

'PP'라는 제품은 다양한 등급을 가지고 있으나 'P530C'라는 등급은 전략적으로 관리하고자 하여 등급별로 분석하고,

나머지는 기타로 모아주기를 원한다고 생각하자

 

이런 경우에는 CASE와 GROUP BY를 활용하여 풀어보자

 

SELECT

   CASE WHEN 제품 = ‘HP’ THEN 제품

   WHEN 제품 = ‘LD’ THEN 등급 ,

   WHEN 등급 = ‘P530C’ THEN 등급

   ELSE ’기타‘

   END

FROM 매출테이블

WHERE 매출일자 LIKE ’9808%’

GROUP BY CASE WHEN 제품 = ‘HP’ THEN 제품

                                  WHEN 제품 = ‘LD’ THEN 등급 ,

                                  WHEN 등급 = ‘P530C’ THEN 등급

ELSE '기타'

END

 

 

SALE_HIST 테이블을 참조하여 ‘PENCIL’ 총 판매금액과 01번 사업장의 총판매금액과

ERASER를 판매하고 있는 사업장의 총판매 금액을 출력하고 그 나머지를 기타로 묶어 총 판매금액을 출력하시오

SELECT

   CASE WHEN SALE_ITEM = 'PENCIL' THEN SALE_ITEM

   WHEN SALE_ITEM = 'ERASER' THEN SALE_SITE

   WHEN SALE_SITE = 01 THEN SALE_SITE

   ELSE '기타'

   END AS 특별관리항목,

   SUM(SALE_AMT) AS 총판매금액

FROM SALE_HIST

GROUP BY CASE WHEN SALE_ITEM = 'PENCIL' THEN SALE_ITEM

                                  WHEN SALE_ITEM = 'ERASER' THEN SALE_SITE

                                  WHEN SALE_SITE = 01 THEN SALE_SITE

ELSE '기타'

END

 

 

TEST100 테이블을 참조하여 C2에 해당하는 값의 비율을 구하여라

SELECT C1, C2

,ROUND(RATIO_TO_REPORT(C2) OVER(),2) * 100 AS PER

FROM TEST100

 

 

TEST35 테이블을 참조하여 KEY1은 부서라고 생각하고 KEY2의 A는 판매액 B는 매입액이라고 할 때

부서별 판매액과 매입액의 차에대한 값(수익)을 구하고 차액의 비율을 구하시오

SELECT

CASE WHEN KEY1 IS NOT NULL THEN KEY1 ELSE '합계' END AS KEY1

   , SUM(CASE WHEN KEY2 = 'A' THEN AMT ELSE 0 END) AS 판매액

   , SUM(CASE WHEN KEY2 = 'B' THEN AMT ELSE 0 END) AS 매입액

   , SUM(CASE WHEN KEY2 = 'A' THEN AMT ELSE - AMT END) AS 수익

   , ROUND(SUM(CASE WHEN KEY2='A' THEN AMT ELSE -AMT END)/ SUM(CASE WHEN KEY2='A' THEN AMT END)*100 )AS PER

FROM (SELECT KEY1, KEY2, SUM(AMT) AMT

               FROM TEST35

               GROUP BY ROLLUP(KEY1), KEY2

)

GROUP BY CASE WHEN KEY1 IS NOT NULL THEN KEY1 ELSE '합계' END

ORDER BY KEY1

728x90
반응형
728x90
반응형

8. 부서번호가 50인 사원들 중 이 부서의 평균 급여액보다 낮은 월급을 받는 사원 명단을 추출하시오

SELECT a.employee_id, a.last_name, a.salary

FROM ( SELECT employee_id, manager_id, salary, last_name

FROM employees WHERE department_id = 50 ) a,

 

(SELECT AVG (salary) avg_salary

FROM employees WHERE department_id = 50 ) b

WHERE a.salary < b.avg_salary

 

 

10. 9번의 출력문을 다시 with을 사용하여 다시 작성하시오

WITH a AS  (SELECT employee_id, manager_id, salary, last_name

                         FROM employees

                         WHERE department id = 50 ),

 

             b AS (SELECT AVG(salary) avg_salary

                         FROM employees

                         WHERE department id = 50)

SELECT a.employee id, a.last_name, a.salary

FROM a, b

WHERE a. salary < b.avg_salary

 

 

11. 전체 부서별 평균 급여액보다 부서별 급여 합계액이 큰 부서의 명단을 추출해 보자 단 서브쿼리

SELECT d.d_dep, d.SUM_sal

FROM

(SELECT a.department_name d_dep, ROUND(SUM(b.salary)) SUM_sal

FROM departments a

, employees b

WHERE a.department_id = b.department_id

GROUP BY a.department_name

) d,

(SELECT ROUND(SUM(sum(b.salary)) / COUNT(*)) AVG_sal

FROM departments a

, employees b

WHERE a.department_id = b.department_id

GROUP BY a.department_name

) c

WHERE d.SUM_sal > c.AVG_sal;

 

 

12 위의 방법을 다시 WITH를 이용하여 다시 작성하시오

WITH dept_costs AS (SELECT department_name, SUM(salary) dept_total

                                            FROM employees e, departments d

                                            WHERE e.department_id = d.department_id

                                            GROUP BY department_name ),

            avg_cost AS (SELECT SUM(dept_total) /COUNT (*) avg

                                       FROM dept_costs)

 

SELECT dept_costs.*

FROM dept_costs, avg_cost

WHERE dept_costs.dept_total > avg_cost.avg

 

 

13. WITH은 SELECT 절과 같이 쓰여 아주 유용한 기능을 제공한다.

10장에서 계층형 쿼리에 대해 학습했는데, 계층형 쿼리를 이용하여 EMPLOYEES 테이블에 있는 사원들을 계층적으로 조회하였다

 

SELECT LEVEL, LPAD(' ', 4* (LEVEL -1)) || first_name || ' ' || last_name "성명"

FROM employees

START WITH MANAGER_ID IS NULL

CONNECT BY manager_id = PRIOR employee_id;

 

계층형 정보를 꼭 CONNECT BY 절을 사용해서만 구현할 수 있는 것은 아니다. WITH 구문을 사용하여 위의 결과와 동일한 데이터를 추출하는 쿼리를 작성해 보자.

 

(힌트: 실제 EMPLOYEES 테이블의 사원 계층은 총 4레벨까지 존재하므로, 레벨 별로 인라인 뷰를 4개 생성해서 WITH을 사용해 생성한 인라인 뷰에서 다른 인라인 뷰를 참조하는 형태로 작성하자. 문제는 난 이도가 꽤 높다. 따라서 이 문제를 풀 정도먼 WITH 구문을 완전히 이해했다고 볼 수 있다 )

 

 

15. 0RDERS 테이블을 참조하여 연도별로 주문건수와 총 주문금액을 구하는 쿼리를 작성해 보자.

 

 

 

16. 1번 쿼리를 기준으로 해서 다음과 같은 형태의 정보를 조회하는 쿼리를 작성해 보자.

기준연도 주문건수 주문총액 이전연도_주문총액 이후연도_주문총액

xxxx 00 0000 0000000 000000

SELECT order_date, order_status, order_total,

SUM(order_total) OVER ( ORDER BY order_date

                                                    RANGE interval '1' year preceding ) AS 이전연도_총합계,

SUM(order_total) OVER ( ORDER BY order_date

                                                    RANGE BETWEEN CURRENT row AND interval '1' year following) AS 이후년도_총합계

FROM ORDERS

 

단 이전연도와 이후연도는 기준연도에서 1년을 더하거나 뺀 연도가 아니라

실제 집계된 데이터가 존재하는 연도를 말한다.

 

 

17. 1999년도 전체 영업실적 중 각 개인별 실적 비율을 구해보자

전체 실적금액으로 각 개인별 실적금액을 니누면 된다 윈도우 함수를 중 분석함수를 사용해서 풀어보자

SELECT emp.last_name 이름, SUM (ord.ORDER_total) 개인별실적,

ROUND(SUM(ord.order_total) / SUM(SUM(ord.order_total)) OVER( PARTITION BY TO_CHAR(ord.order_date, 'YYYY' ) ) ,2) 개인별비율

FROM orders ord, employees emp

WHERE TO_CHAR(ord.ORDER_date, 'YYYY') = '1999'

AND ord. sales_rep_id = emp. employee_id

GROUP BY emp.last_name, TO_CHAR(ord.order_date, 'YYYY')

ORDER BY emp.last_name;

 

 

18. 위의 결과물과 같은 출력값을 가질 수 있게 RATIO_TO_REPORT 함수를 써서 출력하시오

SELECT emp.last_name 이름, SUM(ord.order_total) 개인별실적,

ROUND (RATIO_TO_REPORT(SUM(ord.order_total)) OVER (PARTITION BY TO_chAR(ord.order_date, 'YYYY' )),2) RATIO

FROM orders ord, employees emp

WHERE TO_CHAR(ORD.ORDER_DATE, 'YYYY') = '1999'

AND ord.sales_rep_id = emp.employee_id

GROUP BY emp.last_NAME, TO_CHAR(ord.ORDER_DATE, 'YYYY')

ORDER BY emp.last_name;

728x90
반응형
728x90
반응형

1. TEST12 테이블을 참고하여 BOOK_TYPE별 가격 합계액을 구하시오 (BOOK_TYPE, PRICE만 출력)

SELECT BOOK_TYPE, SUM(PRICE)

FROM TEST12

GROUP BY BOOK_TYPE

 

 

2. 1번의 답을 참고하며, 이번에는 BOOK_TYPE의 소계를 구하시오 (BOOK_TYPE, PRICE만 출력)

SELECT BOOK_TYPE, SUM(PRICE)

FROM TEST12

GROUP BY BOOK_TYPE, ROLLUP(BOOK_NAME)

 

 

3. 1번의 답을 찹고하며, 이번에는 BOOK_TYPE별 가격 합계액을 구하시오

(BOOK_TYPE, PRESS, BOOK_NAME의 3개 컬럼의 정보를 출력하시오)

SELECT PRESS, BOOK_TYPE, SUM(PRICE)

FROM TEST12

GROUP BY PRESS, ROLLUP(BOOK_TYPE)

 

 

4. EMP, DEPT 테이블을 참고하여 부서별로 총 인원수와 급여 합계를 구하시오 (DNAME 출력하시오)

SELECT b.dname, SUM(a.sal) sal, COUNT(a.empno) emp_count

FROM emp a, dept b

WHERE a.deptno = b.deptno

GROUP BY b.dname

 

 

5. EMP, DEPT 테이블과 4번을 참고하여 부서이름과 직업별로 급여소계와 인원소계를 구하고

총인원과 총 급여를 구하여라 단, 부서번호, 부서이름, 직업, 급여, 인원수를 출력하시오

SELECT B.DEPTNO,b.dname,a.job

, SUM(a.sal) sal

, COUNT(a.empno) emp_count

FROM emp a, dept b

WHERE a.deptno = b.deptno

GROUP BY ROLLUP(B.DEPTNO,(b.dname,a.job))

 

 

6. 5번의 출력값에 GROUPING 함수를 사용해서 NULL값을 컬럼의 정보를 표시할 수 있는 문자열을 추가하시오

SELECT DECODE(B.DEPTNO, NULL, '전체합계', B.DEPTNO) AS 부서번호

                , DECODE(b.dname, NULL, '부서소계', b.dname) AS 부서이름

                , DECODE(a.job, NULL, '직업소계', a.job) AS 직업명

                , SUM(a.sal) sal

                , COUNT(a.empno) emp_count

FROM emp a, dept b

WHERE a.deptno = b.deptno

GROUP BY ROLLUP(B.DEPTNO,(b.dname,a.job))

 

 

7. 주문방법, 결혼여부, 성별,주문금액정보와 주문방법별, 결혼여부별, 성별별 주문합계금액을 출력하시오(group sets)

SELECT ORD.ORDER_mode 주문방법,

    DECODE (cus.marital_status, 'single', '미혼', 'married', '기혼') 결혼여부,

    DECODE (cus.gender, 'F', '여성', 'M', '남성') 성별,

    SUM (ord.order_total) 주문금액

FROM orders ord, customers cus

WHERE ord.customer_id = cus.customer_id

GROUP BY GROUPING SETS (ord.order_mode, cus.marital_status, cus.gender);

 

 

 

728x90
반응형
728x90
반응형

WITH

WITH 구문의 사용 (ORACLE9i R2이후 사용 가능)

WITH 문장은 인라인 뷰에 별칭(alias)을 부여하는데, 별칭을 부여히는 것으로 끝나지 않고

SELECT 문장에서 별칭이 부여된 인라인 뷰를 시용 기능하게 한다

 

특징

자주사용되는 쿼리를 사용하기전에 WITH절로 미리 쿼리 블록으로 정의한 후 사용한다

서브쿼리문에서 서브쿼리에 의해 메인 쿼리가 실행되기 때문에 서브쿼리문은 성능이 저하된다.

with절은 여러 개의 서브쿼리가 하나의 메인 쿼리에서 사용될 때 생기는 복잡성을 보다 간결하게 정의하여 사용함으로써 서브쿼리에서 발생할 수 있는 성능저하 현상을 방지할 수 있다

 

 

WITH <별칭1> AS ( SELECT 문장1 ),

             <별칭2> AS ( SELECT 문장2 FROM 별칭1 ),

 

SELECT 컬럼1, 걸림2,

FROM 별칭1, 별칭2

 

 

WITH 구문은 별칭2의 인라인 뷰를 FROM 절에서 바로 이전에 시용한 별칭1을 직접 사용할 수 있는 점이

WITH 구문만이 가진 고유한 특징이다.

 

 

 

728x90
반응형
728x90
반응형

RATIO_TO_REPORT

 

RATIO_TO_REPORT는 계산 대상 값 전체에 대한 현재 로우의 상대적인 비율 값을 반환하는 함수이다.

 

SELECT department_id, First_name, hire_date, salary,

                 ROUND(RATIO_TO_REPORT(salary) OVER (PARTITION BY department_id),2) * 100 AS salary_percent

FROM employees

WHERE department_id IN (30, 90);

 

RATIO_TO_REPORT 함수를 사용하면 굳이 나눗셈을 수행하지 않아도 된다

RATIO_TO_REPORT 함수는 각 로우별로 PARTITION BY 절에 명시된 그룹의 총합에 대한 비율을 반환하는 함수이다.

즉 위 쿼리에서 수행했던 나눗셈을 이 함수 하나가 담당하게 된다

 

 

 

728x90
반응형
728x90
반응형

ROLLUP

ROLLUPSELECT한 컬럼들을 GROUP BY 절과 함께 사용하게 되면 소계를 구할 수 있다

그 과정에는 CARTESIAN PRODUCT를 이용한 결과물들이란 것을 알아두자

또 한 ROLLUP의 인수는 계층 구조이므로 인수 순서가 바뀌면 수행 결과바뀌게되므로 인수의 순서에도 주의하자

 

SELECT PRESS, BOOK_TYPE, BOOK_NAME, SUM(PRICE)

FROM TEST12

GROUP BY ROLLUP(PRESS, ROLLUP(BOOK_TYPE, BOOK_NAME)

 

위의 예제를 보면 알 수 있듯이 ROLLUP은 GROUP BY절에 사용하며 그룹별로 묶어주는 개념비슷하다

그리고 이 때 소계를 출력하고 싶다면 컬럼들을 ROLLUP으로 묶어주면 간단히 출력이 가능하다

 

CUBE

CUBE는 결합이 가능한 모든 값에 대하여 다차원 집계를 생성한다

GROUPING COLUMNS이 가질 수 있는 모든 경우에 대하여 SUBTOTAL을 생성해야 하는 경우에는 CUBE를 사용

ROLLUP에 비해 시스템에 많은 부담을 주므로 사용에 주의해야 한다

 

SELECT PRESS, BOOK_TYPE, BOOK_NAME, SUM(PRICE)

FROM TEST12

GROUP BY CUBE(PRESS, BOOK_TYPE, BOOK_NAME)

 

위의 구문을 실행해보고 ROLLUP과 차이점을 생각해보자

 

 

GROUPING 함수

ROLLUP, CUBE, GROUPING SET등 새로운 그룹 함수를 지원하기위해 GROUPING 함수가 추가되었다

 

-ROLLUP이나 CUBE에 의한 소계가 계산된 결과에는 GROUPING(EXPR) = 1이 표시되고

-그 외의 결과는 GROUPING(EXPR) = 0이 표시된다

 

SELECT PRESS, GROUPING(PRESS), BOOK_TYPE, GROUPING(BOOK_TYPE),

BOOK_NAME, GROUPING(BOOK_NAME),SUM(PRICE)

FROM TEST12

GROUP BY ROLLUP(PRESS, BOOK_TYPE, BOOK_NAME)

 

출력값을 확인하자

 

 

GROUPING_ID(expr)와 GROUP ID() 함수

루트 노드의 경우는 1그 디음 계층은 2 3을 순차적으로 반환하였다. 그룹핑 쿼리에서도 그룹핑되어 추출된 각각의 로우들에 대해 그룹핑 레벨을 알 수 있는데 바로 GROUPING_IDO 함수를 시용하면 된다

 

SELECT

      DECODE (GROUPING (department_id) , 1, ' 전체부서', department_id) DEP,

      DECODE (GROUPING (job_id), 1, '합계', job_id) JOB,

      SUM (salary),

      GROUPING_ID (department_id) g_dep,

      GROUPING_ID (job_id) g_job,

      GROUPING_ID (department_id, job_id) g_total

FROM employees

WHERE department_id <= 40

GROUP BY CUBE(department_id, job_id)

ORDER BY 1,2;

 

결국 G_TOTAL 컬럼값은 0에서 3까지의 수를 반환하게 되는 것이다.

GROUPING_ID 함수의 표현식으로 부서번호와 직급 순서를 변경하면 G_TOTAL 컬럼이 반환하는 값도 달라지게 된다.

만약 위 쿼리에서 세부적인 정보는 제거하고 부서별 합계액직급별 합계액만 조회하고자 한다면 어떻게 해야 할까?

다음과 같이 HAVING 절에 GROUPING_ID() 함수사용하여 조건을 주면 된다

 

SELECT

   DECODE(GROUPING(department_id), 1, '전체부서 ', department_id) DEP,

   DECODE (GROUPING(job_id), 1, '합계', job_id) JOBS,

   SUM (salary)

FROM employees

WHERE department_id <= 40

GROUP BY CUBE (department_id, job_id)

HAVING GROUPING_ID(department_id, job_id) > 0 ORDER BY 1, 2;

 

 

GROUPING SETS(expr) 표현식

쿼리는 단순히 주문방법, 결혼여부, 성별 별로 주문금액을 집계한 것이다.

SELECT ORD.ORDER_mode 주문방법,

   DECODE (cus.marital_status, 'single', '미혼', 'married', '기혼') 결혼여부,

   DECODE (cus.gender, 'F', '여성', 'M', '남성') 성별,

   SUM (ord.order_total) 주문금액

FROM orders ord, customers cus

WHERE ord.customer_id = cus.customer_id

GROUP BY GROUPING SETS (ord.order_mode, cus.marital_status, cus.gender);

 

위의 GROUP함수의 결과값을 비교해보자

GROUPING SETS은 CUBE를 사용한 쿼리에서 개별표현식에 대한 전체합계만을 따로 추출한 것과 같다고 볼 수 있다.

 

 

 

 

 

728x90
반응형
728x90
반응형

1. SALE_HIST의 자료로 일자별 품목별로 ‘01’, ‘02’ 사업장 판매 금액합계, ‘03’, ‘04’ 사업장 판매 금액 합을 구하시오

SELECT SAEL_DATE,

                 SUM(CASE WHEN SALE_SITE BETWEEN ‘01’ AND ‘02’ THEN SALE_AMT END) AS S01

               , SUM(CASE WHEN SALE_SITE BETWEEN ‘03’ AND ‘04’ THEN SALE_AMT END) AS S02

FROM SALE_HIST

GROUP BY SALE_DATE

 

 

2. TEMP의 자료를 이용해 한 행에 5명의 사번과 성명을 보여주는 QUERY를 작성해 보라.

SELECT CEIL(ROWNUM/5) C0

,MAX(DECODE(MOD(ROWNUM, 5), 1, EMP_ID, NULL)) C1

,MAX(DECODE(MOD(ROWNUM, 5), 1, EMP_NAME, NULL)) C2

,MAX(DECODE(MOD(ROWNUM, 5), 2, EMP_ID, NULL)) C3

,MAX(DECODE(MOD(ROWNUM, 5), 2, EMP_NAME, NULL)) C4

,MAX(DECODE(MOD(ROWNUM, 5), 3, EMP_ID, NULL)) C5

,MAX(DECODE(MOD(ROWNUM, 5), 3, EMP_NAME, NULL)) C6

,MAX(DECODE(MOD(ROWNUM, 5), 4, EMP_ID, NULL)) C7

,MAX(DECODE(MOD(ROWNUM, 5), 4, EMP_NAME, NULL)) C8

,MAX(DECODE(MOD(ROWNUM, 5), 0, EMP_ID, NULL)) C9

,MAX(DECODE(MOD(ROWNUM, 5), 0, EMP_NAME, NULL)) C10

FROM TEMP

GROUP BY CEIL(ROWNUM/5)

 

 

3. TEST14와 TEST13을 이용하여 Cartesian Product을 이용하여 테이블을 조인하시오

SELECT *

FROM TEST14 , TEST13

 

 

4. 고객이 어떤 해당 상품을 받아야 하는지 TEST14와 TEST13을 이용하여 고객과 상품LIST를 출력하시오

두 테이블은 POINT에 관련이 있다

SELECT A.CUST, B.GIFT

FROM TEST14 A, TEST13 B

WHERE A.POINT > 0

AND A.POINT BETWEEN B.FPOINT AND B.TPOINT

 

 

5. TEST14와 TEST13을 이용하여 상품별로 준비되어야 할 개수를 알아보는 문장을 작성 하시오

SELECT B.GIFT, COUNT(A.CUST)

FROM TEST14 A, TEST13 B

WHERE A.POINT > 0

AND A.POINT BETWEEN B.FPOINT AND B.TPOINT

GROUP BY B.GIFT

 

 

6.고객이 가진 포인트보다 하한금액이 낮은 모든 등급의 상품을 선택할 수 있다고 할 때 갈비세트를 상품으로 받을 수 있는 고객의 고객번호, 포인트, 선물을 보여주는 QUERY를 만들어보라

SELECT *

FROM TEST14 A, TEST13 B

WHERE B.FPOINT <=  A.POINT

AND B.GIFT ='갈비세트’

 

CREATE TABLE LOGTABLE(USERID VARCHAR(10), STIME VARCHAR(5), ETIME VARCHAR(5));

 

INSERT INTO LOGTABLE

SELECT '사용자1', '07:00', '11:00'

SELECT '사용자2', '10:00', '15:00'

SELECT '사용자3', '11:00', '12:00';

 

CREATE TABLE SETIME(STIME VARCHAR(5), ETIME VARCHAR(5));

 

INSERT INTO SETIME VALUES('00:00', '01:00');

INSERT INTO SETIME VALUES('01:00', '02:00');

INSERT INTO SETIME VALUES('02:00', '03:00');

INSERT INTO SETIME VALUES('03:00', '04:00');

INSERT INTO SETIME VALUES('04:00', '05:00');

INSERT INTO SETIME VALUES('05:00', '06:00');

INSERT INTO SETIME VALUES('06:00', '07:00');

INSERT INTO SETIME VALUES('07:00', '08:00');

INSERT INTO SETIME VALUES('08:00', '09:00');

INSERT INTO SETIME VALUES('09:00', '10:00');

INSERT INTO SETIME VALUES('10:00', '11:00');

INSERT INTO SETIME VALUES('11:00', '12:00');

INSERT INTO SETIME VALUES('12:00', '13:00');

INSERT INTO SETIME VALUES('13:00', '14:00');

INSERT INTO SETIME VALUES('14:00', '15:00');

INSERT INTO SETIME VALUES('15:00', '16:00');

INSERT INTO SETIME VALUES('16:00', '17:00');

INSERT INTO SETIME VALUES('17:00', '18:00');

INSERT INTO SETIME VALUES('18:00', '19:00');

INSERT INTO SETIME VALUES('19:00', '20:00');

INSERT INTO SETIME VALUES('20:00', '21:00');

INSERT INTO SETIME VALUES('21:00', '22:00');

INSERT INTO SETIME VALUES('22:00', '23:00');

INSERT INTO SETIME VALUES('23:00', '24:00');

 

 

7. 위의 구문을 INSERT하고 출퇴근 시간대별로 몇 사람이 출퇴근했는지 사람 수와 출퇴근 시간을 출력하여라

SELECT S.STIME || '-' || S.ETIME, COUNT(*)

FROM LOGTABLE L, SETIME S

WHERE L.STIME < S.ETIME AND L.ETIME >= S.STIME

GROUP BY S.STIME || '-' || S.ETIME

ORDER BY S.STIME || '-' || S.ETIME

 

 

TEST34 활용 문제 8, 9

INSERT INTO TEST34 VALUE ('A10',4,100);

INSERT INTO TEST34 VALUE ('A11',4,110);

COMMIT;

8. 위 구문을 인설트하고 KEY_TYPE이 ‘4’인 경우에 ‘1’, ‘2’, ‘3’ 에 모두 더하고 ‘1’의 합과 ‘2’, ‘3’의 합을 구해라

선택적 조인문과 DECODE를 활용하시오

SELECT T2.R_CNT, SUM(AMT) AMT

FROM TEST34 T1, (SELECT ROWNUM R_CNT

                                       FROM TEST34

                                       WHERE ROWNUM<4) T2

WHERE T1.KEY_TYPE = DECODE(T1.KEY_TYPE, 4, T1.KEY_TYPE, T2.R_CNT)

GROUP BY T2.R_CNT;

 

 

key type amt rcnt

A01 1 10 1 true

A03 1 30 1 tur

A07 1 70 1

A09 1 90 1

A10 4 100 1 true

A11 4 110 1

410

 

A02 2 20 2

A04 2 40 2

A08 2 80 2

A10 4 100 2

A11 4 110 2

350

 

A05 3 50 3

A06 3 60 3

A10 4 100 3

A11 4 110 3

320

 

 

9. 위 구문을 인설트하고 KEY_TYPE이 ‘4’인 경우에 ‘1’, ‘2’, ‘3’ 에 모두 더하고 ‘1’, ‘2’, ‘3’의 합을 구해라

선택적 조인문과 윈도우 함수를 활용하시오 LAG()

SELECT *

FROM (SELECT DECODE(KEY_TYPE,1,'A',2,'B',3,'C') AS KEY

               , SUM(AMT) 

                 + LAG(SUM(AMT), DECODE(KEY_TYPE,1,1,2,2,3,3)) OVER (ORDER BY DECODE(KEY_TYPE,1,'A',2,'B',3,'C')

                                                                                                                                      NULLS FIRST) AS AMT

               FROM TEST34

              GROUP BY KEY_TYPE

              )

WHERE KEY IS NOT NULL

 

 

 

728x90
반응형

+ Recent posts

Powered by Tistory, Designed by wallel