Search

11-4. 분석함수와 PARTITION BY

11-4-1. PARTITION BY

분석함수는 분석대상에 대해 분석을 수행한다. 분석함수의 OVER 절에 PARTITION BY와 ORDER BY, 그리고 RANGE/ROWS를 사용해 레코드별로 분석대상을 세밀하게 나눌 수 있다.
PARTITION BY를 사용해 특정 컬럼의 값에 따라 레코드별로 분석대상을 나눌 수 있다. 아래는 SUM OVER 분석함수를 사용한 SQL이다.
-- [SQL-11-4-1-a] PARTITION BY SELECT T1.ShopId ,T1.OrdNo ,T1.OrdDtm ,T1.OrdAmt ,SUM(T1.OrdAmt) OVER() SumOverOrdAmt ,SUM(T1.OrdAmt) OVER(PARTITION BY T1.ShopId) SumOverOrdAmtByShop FROM startdb.Ord T1 WHERE T1.OrdDtm >= STR_TO_DATE('20220101','%Y%m%d') AND T1.OrdDtm < STR_TO_DATE('20220110','%Y%m%d') AND T1.ShopId IN ('S195','S027') ORDER BY T1.ShopId ,T1.OrdDtm ASC; ShopId OrdNo OrdDtm OrdAmt SumOverOrdAmt SumOverOrdAmtByShop ------ ----- ------------------- -------- ------------- ------------------- S027 25441 2022-01-05 11:00:00 7500.000 28500.000 12000.000 S027 25485 2022-01-05 12:00:00 4500.000 28500.000 12000.000 S195 25285 2022-01-02 09:30:00 8000.000 28500.000 16500.000 S195 25443 2022-01-05 11:30:00 8500.000 28500.000 16500.000
SQL
복사
SumOverOrdAmt (전체 주문 금액)
SUM(T1.OrdAmt) OVER()
OVER() 절 내에 아무런 파티션 구분이 지정되지 않았기 때문에, 전체 레코드를 대상으로 주문 금액의 합계를 계산한다.
모든 레코드에서 계산된 합계 값은 동일하며, 이는 전체 주문 금액의 총합을 나타낸다.
SumOverOrdAmtByShop (매장별 주문 금액)
SUM(T1.OrdAmt) OVER(PARTITION BY T1.ShopId)
OVER() 절 내에 PARTITION BY T1.ShopId가 포함되어 있어, ShopId별로 데이터를 그룹화하여 분석한다.
이 구분에 따라 각 매장 ShopId가 같은 레코드들끼리 그룹화되어 그 그룹 내에서만 주문 금액의 합계가 계산된다.
결과적으로 각 매장별로 주문 금액의 합계가 계산되며, 같은 ShopId를 가진 레코드들은 동일한 합계 값을 가집니다.
PARTITION BY는 GROUP BY와 달리, 같은 값을 가진 레코드를 하나로 모으지 않고 각각 독립적으로 유지한다. 분석함수는 데이터의 건수를 변화시키지 않으면서, 각 레코드를 개별적으로 분석할 수 있도록 해준다는 것을
GROUP BY 처럼 같은 값을 가진 레코드를 모아서 한 건으로 표현하지 않는다. 분석함수는 레코드의 건수를 변형시키지 않은 채 분석을 수행하는 특징이 있다.

11-4-2. PARTITION BY ~ ORDER BY

OVER 절에는 PARTITION BY와 ORDER BY를 동시에 사용할 수 있다. RANK OVER 분석함수에 PARTITION BY를 추가하면 파티션별로 순위를 구할 수 있다. 아래는 ShopOperTp 별로 TableQty에 따른 순위를 구하는 SQL이다. PARTITION BY를 먼저 정의하고 ORDER BY를 뒤에 정의한다.
-- [SQL-11-4-2-a] 파티션별 테이블수량 순위 구하기 SELECT T1.ShopID ,T1.ShopNm ,T1.TableQty ,T1.ShopOperTp ,RANK() OVER(ORDER BY T1.TableQty DESC) RankTableQty ,RANK() OVER(PARTITION BY T1.ShopOperTp ORDER BY T1.TableQty DESC) RankTableQtyByOperTp FROM startdb.Shop T1 WHERE T1.ShopStartYmd = '20180409' ORDER BY T1.ShopOperTp ASC, T1.TableQty DESC; ShopID ShopNm TableQty ShopOperTp RankTableQty RankTableQtyByOperTp ------ ---------------- -------- ---------- ------------ -------------------- S049 Dallas-3rd 21 DIST 1 1 S006 Philadelphia-1st 9 DIST 4 2 S048 San Diego-3rd 20 DRCT 2 1 S007 San Antonio-1st 10 FLAG 3 1 S021 New York-2nd 7 FLAG 5 2 S020 Washington-1st 6 FLAG 6 3
SQL
복사
PARTITION BY에도 여러 컬럼을 지정할 수 있다. PARTITION BY로 정의한 컬럼들이 끝나고 ORDER BY를 사용할 때는 콤마가 아닌 빈칸으로만 구분한다는 점에 주의하기 바란다.
PARTITION BY COL1, ORDER BY COL3: 비정상
PARTITION BY COL1 ORDER BY COL3: 정상
PARTITION BY COL1, COL2, ORDER BY COL3: 비정상
PARTITION BY COL1, COL2 ORDER BY COL3: 정상
아래는 ShopId, MemberId별로 순위를 구하는 SQL이다.
-- [SQL-11-4-2-b] SELECT T1.ShopId ,T1.MemberId ,T1.OrdDtm ,T1.OrdNo ,ROW_NUMBER() OVER(ORDER BY T1.OrdDtm ASC) RankOverDt ,ROW_NUMBER() OVER( PARTITION BY T1.ShopId ,T1.MemberId ORDER BY T1.OrdDtm ASC) RankOverDtShopMember FROM startdb.Ord T1 WHERE T1.OrdDtm >= STR_TO_DATE('20220101','%Y%m%d') AND T1.OrdDtm < STR_TO_DATE('20220201','%Y%m%d') AND T1.ShopId IN ('S195','S027') ORDER BY T1.ShopId ,T1.MemberId ASC; ShopId MemberId OrdDtm OrdNo RankOverDt RankOverDtShopMember ------ -------- ------------------- ----- ---------- -------------------- S027 M1295 2022-01-05 12:00:00 25485 4 1 S027 M1295 2022-01-11 12:00:00 25652 7 2 S027 M1295 2022-01-23 12:00:00 25837 10 3 S027 M1295 2022-01-29 12:00:00 26004 13 4 S027 M2293 2022-01-05 11:00:00 25441 2 1 S027 M2293 2022-01-11 11:00:00 25608 5 2 S027 M2293 2022-01-23 11:00:00 25793 8 3 S027 M2293 2022-01-29 11:00:00 25960 11 4 S195 M0207 2022-01-05 11:30:00 25443 3 1 S195 M0207 2022-01-11 11:30:00 25610 6 2 S195 M0207 2022-01-23 11:30:00 25795 9 3 S195 M0207 2022-01-29 11:30:00 25962 12 4 S195 M1210 2022-01-02 09:30:00 25285 1 1 S195 M1210 2022-01-30 09:30:00 26054 14 2
SQL
복사
이처럼 RANK 분석함수에 PARTITION BY를 적용하면 특정 그룹별 순위를 쉽게 구할 수 있다.

11-4-3. PARTITION BY 컬럼의 변형

컬럼을 변환해서 GROUP BY에 사용할 수 있는 것처럼, PARTITION BY의 컬럼도 변환해서 사용이 가능하다. 아래는 년월별로 상품별 주문수량 순위를 구해서 Top-3 까지만 출력하는 SQL이다.
-- [SQL-11-4-2-c] 주문년월별 Top-3 상품 SELECT T5.* FROM ( SELECT T4.OrdYm ,T4.ItemId ,T4.ItemNm ,T4.OrdQty ,RANK() OVER(PARTITION BY T4.OrdYm ORDER BY T4.OrdQty DESC) RankByYm FROM ( SELECT DATE_FORMAT(T1.OrdDtm,'%Y%m') OrdYm ,T3.ItemId ,MAX(T3.ItemNm) ItemNm ,SUM(T2.OrdQty) OrdQty FROM startdb.Ord T1 INNER JOIN startdb.OrdDet T2 ON (T2.OrdNo = T1.OrdNo) INNER JOIN startdb.Item T3 ON (T3.ItemID = T2.ItemId) WHERE T1.OrdDtm >= STR_TO_DATE('20230101','%Y%m%d') AND T1.OrdDtm < STR_TO_DATE('20230301','%Y%m%d') GROUP BY DATE_FORMAT(T1.OrdDtm,'%Y%m') ,T3.ItemId ) T4 ) T5 WHERE T5.RankByYm <= 3; OrdYm ItemId ItemNm OrdQty RankByYm ------ ------ ------------------ ------ -------- 202301 IAMR Iced Americano(R) 287 1 202301 ICLR Iced Cafe Latte(R) 286 2 202301 AMR Americano(R) 286 2 202302 ICLR Iced Cafe Latte(R) 299 1 202302 AMR Americano(R) 299 1 202302 IAMR Iced Americano(R) 298 3
SQL
복사

BOOSTER QUIZ

BOOSTER QUIZ ERD

BOOSTER QUIZ 11-4-1

2023년 1월 주문에서 M0200,M0201,M0202 회원의 데이터만 조회해주세요. 주문 리스트와 회원별 주문금액합계를 컬럼으로 추가해 보여주세요.
대상 테이블: 주문(Ord)
조회 조건: OrdDtm이 2023년 1월이면서 MemberId가 M0200,M0201,M0202인 데이터
조회 컬럼: MemberId, OrdDtm, OrdAmt, SumOverAmtMember
추가 조건
SumOverAmtMember는 조회된 데이터에서 회원별 주문금액 합계입니다. 분석함수에 PARTITION을 적용해 구합니다.
MemberId, OrdDtm으로 오름차순 정렬해주세요.
MemberId OrdDtm OrdAmt SumOverAmtMember -------- ------------------- -------- ---------------- M0200 2023-01-02 10:00:00 4500.000 12500.000 M0200 2023-01-30 10:00:00 8000.000 12500.000 M0201 2023-01-05 10:00:00 9000.000 27500.000 M0201 2023-01-11 10:00:00 9500.000 27500.000 M0201 2023-01-23 10:00:00 4500.000 27500.000 M0201 2023-01-29 10:00:00 4500.000 27500.000 M0202 2023-01-02 11:00:00 4500.000 9500.000 M0202 2023-01-30 11:00:00 5000.000 9500.000
SQL
복사

BOOSTER QUIZ 11-4-2

2023년 1월부터 2023년 3월 주문에 대해, 주문년월별로 주문금액이 가장 높은 매장 하나씩을 조회해주세요.
대상 테이블: 매장(Shop), 주문(Ord)
조회 조건: OrdDtm이 2023년 1월부터 2023년 3월까지인 주문 데이터
조회 컬럼: OrdYm, ShopId, ShopNm, SumOrdAmt, RankByYm
추가 조건
단계적으로 SQL을 작성합니다.
첫 번째 인라인 뷰(IV1)
OrdYm은 OrdDtm을 년월형태로 변환한 값이다.
ShopId별 OrdYm별 GROUP BY 처리하시오
SumOrdAmt는 OrdYm, ShopId별 OrdAmt를 SUM한 값
두 번째 인라인 뷰(IV2)
첫 번째 인라인 뷰(IV1)를 활용해 RankByYm 구하기
RankByYm은 OrdYm별로 OrdAmt에 따른 순위
RANK 분석함수와 PARTITION BY를 사용해 구한다.
메인 쿼리
두 번째 인라인 뷰(IV2)에서 RankByYm이 1인 데이터만 조회
OrdYm ShopId ShopNm SumOrdAmt RankByYm ------ ------ ------------- ---------- -------- 202301 S230 San Jose-12th 210000.000 1 202302 S244 Houston-13th 173500.000 1 202303 S134 Columbus-7th 557000.000 1
SQL
복사