DataBase/SQL
SQL BOOSTER - GROUP BY와 ROLLUP
now0204
2024. 6. 2. 18:37
프로젝트를 수행하는데 최소한의 SQL임! 잘 알아두자
1. GROUP BY
- 데이터를 그룹화하는 문법이다. (같은 값을 가진 데이터끼리 모으는 것을 의미)
- 중복된 값이 제거된 결과로 나오면서, 집계함수를 사용한다.
SELECT T1.ORD_DT, T1.PAY_TP,SUM(T1.ORD_AMT)ORD_AMT
FROM T_ORD T1
WHERE T1.ORD_ST = 'COMP'
GROUP BY T1.ORD_DT, T1.PAY_TP
ORDER BY T1.ORD_DT, T1.PAY_TP;
- T1.ORD_DT로 1차 그룹화 후 T1.PAY_TP로 2차 그룹화 (주문일시, 지불유형별 주문 금액 합계)
- 자주 쓰는 집계함수는 다음과 같다.
- SUM,COUNT,MIN,MAX
- OVER 절과 함께 사용하는 SUM,LEAD,LAG와 같은 분석함수는 집계함수와 다른 개념이다.
- 집계함수는 GROUP BY 없이도 단독 사용 가능(단, 집계함수를 사용하지 않은 컬럼 동시 SELECT 불가)
- 모든 ROW 데이터에 대해 집계함수 적용
- 규칙 1 GROUP BY에 사용한 컬럼만 SELECT 절에서 그대로 사용 가능하다.
- 규칙 2 GROUP BY에 사용하지 않은 컬럼은 SELECT절에서 집계함수를 사용해야한다.
1.2 GROUP BY 컬럼 변형
- GROUP BY에 컬럼을 정의할 대 컬럼을 변형할 수 있다. 변형을 위해 TO_CHAR, TO_DATE와 같은 오라클 기본 함수
- CASE와 같은 치환 문법 사용할 수 있다.
- 또한 문자와 문자 결합 혹은 산술연산도 할 수 있다.
SELECT ORD_ST,
CASE WHEN ORD_AMT >= 5000 THEN 'HIGH'
WHEN ORD_AMT >= 3000 THEN 'Middle Order'
ELSE 'Low Order' END ORD_AMT_TP, COUNT(*) ORD_CNT
FROM T_ORD
GROUP BY ORD_ST,
CASE WHEN ORD_AMT >= 5000 THEN 'HIGH'
WHEN ORD_AMT >= 3000 THEN 'Middle Order'
ELSE 'Low Order' END
ORDER BY 1,2;
- CASE 처리를 통해 GROUP BY를 하고 있다.
- ORDER BY에서 사용한 컬럼 + 데이터 명 SELECT에서 사용한 컬럼 + 데이터 명 일치해야한다. (규칙 1)
- 1회성 쿼리에선 주문금액을 CASE로 나누는 위와 같은 SQL이 괜찮지만, 운영할 때는 지양하자
- ORD_AMT의 기준이 변경되면 SQL을 모두 변경해야하기 때문이다.
SELECT TO_CHAR(ORD_DT,'YYYYMM') ORD_YM, PAY_TP, COUNT(*) ORD_CNT
FROM T_ORD
WHERE ORD_ST = 'COMP'
GROUP BY TO_CHAR(ORD_DT,'YYYYMM'), PAY_TP
ORDER BY TO_CHAR(ORD_DT,'YYYYMM'), PAY_TP;
- 위는 주문일시를 YYYYMM형태로 변경해서 주문년월별 주문 건수 조회하는 SQL이다.
1.3 집계함수에서 CASE문 사용하기
집계함수 괄호 안에서도 CASE문을 사용할 수도 있다.
이를 사용하면, CASE 조건에 따라 집계를 수행할 수 있다.
SELECT TO_CHAR(ORD_DT,'YYYYMM') ORD_YM, SUM(CASE WHEN PAY_TP = 'BANK' THEN 1 END) BANK_PAY_CNT,
SUM(CASE WHEN PAY_TP ='CARD' THEN 1 END) CARD_PAY_CNT
FROM T_ORD
WHERE ORD_ST = 'COMP'
GROUP BY TO_CHAR(ORD_DT,'YYYYMM')
ORDER BY TO_CHAR(ORD_DT,'YYYYMM');
- 주문년월별로 계좌이체 건수와 카드결제 건수'를 조회하는 SQL이다.
- CASE에 따라 sum에 1을 넘겨서 계산하는 방식은 매우 자주 사용하니 잘 봐두자
201701 PAY_TP CARD, BANK -> 집계함수 -> 201701 PAY_TP BANK (1) PAY_TP CARD(1)
SELECT PAY_TP,
COUNT(CASE WHEN TO_CHAR(ORD_DT,'YYYYMM') = '201701' THEN 'X' END) ORD_CNT_1701
, COUNT(CASE WHEN TO_CHAR(ORD_DT,'YYYYMM') = '201702' THEN 'X' END) ORD_CNT_1702
, COUNT(CASE WHEN TO_CHAR(ORD_DT,'YYYYMM') = '201703' THEN 'X' END) ORD_CNT_1703
, COUNT(CASE WHEN TO_CHAR(ORD_DT,'YYYYMM') = '201704' THEN 'X' END) ORD_CNT_1704
, COUNT(CASE WHEN TO_CHAR(ORD_DT,'YYYYMM') = '201705' THEN 'X' END) ORD_CNT_1705
, COUNT(CASE WHEN TO_CHAR(ORD_DT,'YYYYMM') = '201706' THEN 'X' END) ORD_CNT_1706
, COUNT(CASE WHEN TO_CHAR(ORD_DT,'YYYYMM') = '201707' THEN 'X' END) ORD_CNT_1707
FROM T_ORD
WHERE ORD_ST = 'COMP'
GROUP BY PAY_TP
ORDER BY PAY_TP;
// 혹은 아래와 같은 방식 사용 가능
SELECT t1.PAY_TP, MAX(CASE WHEN t1.ORD_YM='201701' THEN t1.ORD_CNT END) ORD_CNT_1701
,MAX(CASE WHEN t1.ORD_YM='201702' THEN t1.ORD_CNT END) ORD_CNT_1702
,MAX(CASE WHEN t1.ORD_YM='201703' THEN t1.ORD_CNT END) ORD_CNT_1703
,MAX(CASE WHEN t1.ORD_YM='201704' THEN t1.ORD_CNT END) ORD_CNT_1704
,MAX(CASE WHEN t1.ORD_YM='201705' THEN t1.ORD_CNT END) ORD_CNT_1705
FROM (
SELECT t2.PAY_TP, TO_CHAR(t2.ORD_DT,'YYYYMM') ORD_YM,COUNT(*) ORD_CNT
FROM T_ORD t2
WHERE t2.ORD_ST = 'COMP'
GROUP BY t2.PAY_TP, TO_CHAR(t2.ORD_DT,'YYYYMM')
)t1
GROUP BY t1.PAY_TP;
-- CARD 201701 73
CARD 201702 153
-- PAY 201701 67
PAY 201702 79 -> 인라인뷰 결과
-- MAX를 사용한 이유는 인라인뷰 COUNT 결과 가져올 때 집계함수 사용해야하는데 마땅한게 없어서 쓴듯
- 건수를 시간 순서대로 표현했다
- 이렇게 가로에 시간 속성을 보여주는 것이 굉장히 흔한 편이다.
- 물론 컬럼을 로우로 변환하는 PIVOT 기능을 지원하는 BI툴이 많음으로, GROUP BY와 CASE 조합 사용할 일은 많지는 않다.
1.4 COUNT 집계함수
- COUNT는 다른 집계함수와 다르게 조금 더 살펴볼 필요가 있다.
- COUNT(칼럼)는 NULL은 0으로 카운트한다. (칼럼 확인)
- COUNT(*)는 로우를 COUNT로 (로우 갯수 확인)
- SELECT절에서 DISTINCT를 중복제거 가능하다 COUNT(DISTINCT 컬럼)
- COUNT(DISTINCT T1.ORD_ST || '-'|| T1.PAY_TP) 두개 이상 컬럼 DISTINCT 처리
- 혹은 인라인뷰를 사용해야 두 개 이상의 컬럼에 사용할 수 있다.
- 한 번이라도 로그인이 있는 고객수
- 한 번이라도 사용 기록이 있는 메뉴 수
- 한 번이라도 판매가 이루어진 아이템 수
-- 각각 SELECT ~ EXISTS 혹은 DISTICT를 사용하여
SELECT COUNT(*)
FROM 고객 t1
WHERE EXISTS(
SELECT *
FROM 로그인 A
WHERE A.고객ID = T1.고객ID
);
SELECT COUNT(DISTINCT T1.고객ID)
FROM 로그인 T1
2. HAVING
HAVING절은 GROUP BY가 수행된 결과 집합에 조건을 줄 때 사용한다. WHERE절 기능 수행
HAVING은 AND나 OR을 이용해 여러 조건을 동시에 사용할 수도 있다.
HAVING에서는 GROUP BY에서 정의한 내용은 그대로 사용할 수 있으나, GROUP BY에 없는 내용은 집계함수 처리 후에 사용해야한다.
SELECT T1.CUS_ID,T1.PAY_TP,SUM(T1.ORD_AMT) ORD_TTL_AMT
FROM T_ORD T1
GROUP BY T1.CUS_ID, T1.PAY_TP
HAVING T1.ORD_ST ='COMP' -- ERROR
- WHERE절에 사용하거나, T1.ORD_ST에 집계함수를 적용하면 사용할 수 있다.
- 인라인 뷰를 사용해 처리할 수도 있다
SELECT T0.*
FROM (
SELECT T1.CUS_ID,T1.PAY_TP,SUM(T1.ORD_AMT) ORD_TTL_AMT
FROM T_ORD T1
WHERE T1.ORD_ST = 'COMP'
GROUP BY T1.CUS_ID, T1.PAY_TP
)T0
WHERE T0.ORD_TTL_AMT >= 10000
ORDER BY T0.ORD_TTL_AMT ASC;
- 그룹으로 묶고, WHERE절 처리
https://www.yes24.com/Product/Goods/82818767