더북(TheBook)

02 | WITH 절

개선된 서브 쿼리

6장에서 여러 형태의 서브 쿼리를 학습했었는데 서브 쿼리에는 한 가지 제약 사항이있다. 예를 들어, kor_loan_status 테이블에서 연도별 최종월 기준 가장 대출이 많은 도시와 잔액을 구해야 한다고 해보자. 간단히 쿼리를 작성할 수 있을 것 같지만 생각 외로 복잡한 측면이 있으므로 앞에서 배운 “분할해서 정복하라” 기법을 사용해 보자.

연도별 최종: 2011년의 최종년도는 12월이지만 2013년은 11월이므로 연도별 최종월을 알아야 한다.
구현 방안: 그룹 쿼리로 연도별로 가장 큰 월을 구한다. → MAX(period)

ⓑ 연도별 최종월을 대상으로 대출잔액이 가장 큰 금액을 추출해야 한다.
구현 방안: ⓐ와 조인을 해서 연도별로 가장 큰 잔액을 구한다. → MAX(loan_jan_amt)

ⓒ 월별, 지역별 대출잔액과 ⓑ 결과를 비교해 금액이 같은 건을 추출한다.
구현 방안: ⓑ와 조인을 해서 두 금액이 같은 건을 구한다.

위 시나리오대로 작성한 쿼리는 다음과 같다.

입력

    SELECT b2.*
    FROM ( SELECT period, region, sum(loan_jan_amt) jan_amt
             FROM kor_loan_status
             GROUP BY period, region
          ) b2,
          ( SELECT b.period,  MAX(b.jan_amt) max_jan_amt
             FROM ( SELECT period, region, sum(loan_jan_amt) jan_amt
                      FROM kor_loan_status
                     GROUP BY period, region
                  ) b,ⓑ
                  ( SELECT MAX(PERIOD) max_monthⓒ
                      FROM kor_loan_statusⓐ
                     GROUP BY SUBSTR(PERIOD, 1, 4)
                  ) a
             WHERE b.period = a.max_month
             GROUP BY b.period
          ) c
     WHERE b2.period = c.period
       AND b2.jan_amt = c.max_jan_amt
     ORDER BY 1;

결과

    PERIOD   REGION     JAN_AMT
    -------- ---------- ----------
    201112   서울       204275.7
    201212   서울       203344.9
    201311   서울       205644.3

생각보다는 쿼리가 좀 복잡해졌는데, 자세히 보면 색으로 표시된 부분(연도, 지역별 잔액총액)은 같은 구문이 두 번 사용되었음을 알 수 있다. 동일한 구문을 한 번만 사용할 수 있다면 쿼리가 훨씬 간단해질텐데, 좋은 방법이 없을까? 기존 서브 쿼리로는 해결할 방법이 없지만 WITH 절을 사용하면 가능하다. WITH 구문의 형식은 다음과 같다.

    WITH 별칭1 AS (SELECT 문),
         별칭2 AS (SELECT 문)
    ...
    SELECT
    FROM 별칭1, 별칭2 ...

WITH이 맨 앞에 오고 별칭을 앞에 명시한다는 점만 제외하면 기본 형태는 일반 서브 쿼리와 별반 다르지 않다. 하지만 WITH 절은, 별칭으로 사용한 SELECT 문의 FROM 절에 다른 SELECT 구문의 별칭 참조가 가능하다.하다.

먼저 위 쿼리를 그대로 WITH 구문 형태로 변경해 보자.

입력

    WITH b2 AS ( SELECT period, region, sum(loan_jan_amt) jan_amt
                   FROM kor_loan_status
                  GROUP BY period, region
               ),
         c AS ( SELECT b.period,  MAX(b.jan_amt) max_jan_amt
                  FROM ( SELECT period, region, sum(loan_jan_amt) jan_amt
                          FROM kor_loan_status
                         GROUP BY period, region
                       ) b,
                       ( SELECT MAX(PERIOD) max_month
                           FROM kor_loan_status
                          GROUP BY SUBSTR(PERIOD, 1, 4)
                       ) a
                 WHERE b.period = a.max_month
                 GROUP BY b.period
               )
    SELECT b2.*
      FROM b2, c
     WHERE b2.period = c.period
       AND b2.jan_amt = c.max_jan_amt
     ORDER BY 1;

WITH 절에서는 색으로 표시한 두 번째 SELECT 문인 b대신 바로 직전에 명시한 서브 쿼리인 b2를 참조할 수 있다.

입력

    WITH b2 AS ( SELECT period, region, sum(loan_jan_amt) jan_amt
                   FROM kor_loan_status
                  GROUP BY period, region
               ),
         c AS ( SELECT b2.period,  MAX(b2.jan_amt) max_jan_amt
                  FROM b2,
                       ( SELECT MAX(PERIOD) max_month
                           FROM kor_loan_status
                          GROUP BY SUBSTR(PERIOD, 1, 4)
                       ) a
                 WHERE b2.period = a.max_month
                 GROUP BY b.period
               )
    SELECT b2.*
      FROM b2, c
     WHERE b2.period = c.period
       AND b2.jan_amt = c.max_jan_amt
     ORDER BY 1;

결과

    PERIOD   REGION     JAN_AMT
    -------- ---------- ------------
    201112   서울       204275.7
    201212   서울       203344.9
    201311   서울       205644.3

쿼리가 훨씬 간단해지면서 동일한 결과가 조회된 것을 확인할 수 있다. 일반 서브 쿼리와 중복 구문이 필요 없는 WITH 절을 그림으로 나타내면 다음과 같다.

그림 7-13 일반 서브 쿼리와 WITH 절

[일반 서브 쿼리] / [WITH 절]

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.