30 Jun 2023
1. 데이터의 그룹화
SELECT group_col_1, group_col_2, agg_func
FROM table_nm
WHERE conditional
GROUP BY col_1, col_2;
-- EX.
-- 원본 데이블
-- SELECT * FROM ENG_SCORE;
-- +-----+----------+-----------+-----------+
-- | ID | ENG | MATH | GENDER |
-- +-----+----------+-----------+-----------+
-- | 김필호 | 28 | 49 | 남자
-- | 이치훈 | 68 | 98 | 남자
-- | 김영숙 | 87 | 87 | 여자
-- | 성은영 | 65 | 78 | 여자
-- +-----+----------+-----------+-----------+
-- 성별 기준 그룹핑
SELECT GENDER,
AVG(ENG) AS ENG_SCORE,
AVG(MATH) AS MATH_SCORE
FROM ENG_SCORE
GROUP BY GENDER;
-- +-----+----------+-----------+
-- | GENDER | ENG_SCORE | MATH_SCORE
-- +-----+----------+-----------+
-- | ENG | 48 | 73.5
-- | MATH | 76 | 82.5
-- +-----+----------+-----------+
TIP
- SELECT문의 출력 대상이 되는 열은 “꼭” GROUP BY 절에 있어야함.
- GROUP BY절에 포함되는 열이 1개씩 증가할 때마다, 차원(Dimension)이 한개씩 증가하고 좀 더 세부적으로 분석 가능하다.
- 순서:
WHERE > GROUP BY > ORDER BY
- 그룹화될 열에 NULL값이 존재할 경우, NULL값도 그룹화 됨.
GROUP BY & HAVING절에선, AS로 설정한 별칭을 활용하여 그룹핑을 할 수 있음.
2. 그룹화된 데이터의 필터링
1) HAVING
: HAVING은 WHERE 조건절과 마찬가지로 조건을 주는 역할을 하는데,
차이는 HAVING은 그룹화된 변수에 대해서만 대해 조건을 줌.
- WHERE절은 개별 레코드에 대한 조건을 검사
- HAVING절은 그룹화된 결과 집합에 조건을 검사
SELECT group_col_1, agg_func
FROM table_nm
WHERE conditional
GROUP BY col_1
HAVING conditional_of_agg_func_output;
-- EX)
-- 1) 부서별 구성원이, 5명 초과인 부서 추출
SELECT SEG,
COUNT(*) AS PART_CNT
FROM PERF_MAST_201312
GROUP BY SEG
HAVING COUNT(*) > 5;
-- +------+----------+
-- | SEG | PART_CNT |
-- +------+----------+
-- | MASS | 6 |
-- +------+----------+
-- 2) 부서별 직전달 구매금액 총액이, 500,000원 이상인 부서 추출
SELECT SEG,
SUM(TOT_AMT_1) AS TOT_AMT_1_SUM
FROM PERF_MAST_201312
GROUP BY SEG
HAVING SUM(TOT_AMT_1)>= 500000;
-- +-----+---------------+
-- | SEG | TOT_AMT_1_SUM |
-- +-----+---------------+
-- | PB | 11534253 |
-- +-----+---------------+
-- 3) 부서별 직전달 인당 평균 금액이, 200000원 이하인 부서 추출
SELECT SEG,
AVG(TOT_AMT_1) AS TOT_AMT_1_AVG
FROM PERF_MAST_201312
GROUP BY SEG
HAVING AVG(TOT_AMT_1) <= 200000;
-- +------+---------------+
-- | SEG | TOT_AMT_1_SUM |
-- +------+---------------+
-- | MASS | 108726.2500 |
-- +------+---------------+
-- 3) 직전달 금액이 50000인 이상인 부서원에 한하여, 부서별 직전달 인당 평균 금액이, 200000원 이하인 부서 추출
SELECT SEG,
AVG(TOT_AMT_1) AS TOT_AMT_1_SUM
FROM PERF_MAST_201312
WHERE TOT_AMT_1 >= 50000
GROUP BY SEG
HAVING AVG(TOT_AMT_1) <= 200000;
-- +------+---------------+
-- | SEG | TOT_AMT_1_SUM |
-- +------+---------------+
-- | MASS | 144788.3333 |
-- +------+---------------+
TIP
WHERE 조건절의 조건은 데이터가 그룹화 되기 ‘전에’ 필터링을 진행
HAVING 조건절의 조건은 데이터가 그룹화 된 ‘후에’ 필터링을 진행
- 따라서
WHERE조건절에서 제외된 행은, GROUP BY 및 HAVING 조건절 모두에서 제외됨.
+ 연습문제
- PPC_MAST_201312 테이블
+---------------+---------+---------+------+-------------+
| SSN | ACCT_NO | ACCT_CD | PRFT | BALANCE_AMT |
+---------------+---------+---------+------+-------------+
| 7802221111111 | 22033 | 130 | 504 | 56746 |
| 8307153333444 | 54412 | 110 | 585 | 23540 |
| 5605099999222 | 65433 | 340 | 213 | 987800 |
| 8012301111333 | 58721 | 320 | 780 | 310000 |
| 6711032222111 | 23422 | 120 | 5679 | 3 |
| 8910103333222 | 89811 | 310 | 240 | 40011 |
| 7802221111111 | 78022 | 100 | 899 | 4565000 |
| 6711032222111 | 35714 | 300 | 3780 | 2545640 |
| 8910103333222 | 68740 | 310 | 233 | 522312 |
| 5605099999222 | 96870 | 330 | 7000 | 2158 |
| 7802221111111 | 89770 | 140 | 1000 | 566600 |
| 6711032222111 | 33270 | 130 | 5600 | 68770 |
| 7802221111111 | 87890 | 340 | 1270 | 5500000 |
+---------------+---------+---------+------+-------------+
-- 1-1) 자산 & 부채 테이블 생성
-- 수신(부채) 코드 : 100, 110, 120, 130, 140
-- 여신(자산) 코드 : 300, 310, 320, 330, 340
SELECT
CASE
WHEN ACCT_CD IN (100, 110, 120, 130, 140) THEN 'LIABILITY'
WHEN ACCT_CD IN (300, 310, 320, 330, 340) THEN 'ASSET'
ELSE ACCT_CD END AS BALANCE_SHEET,
sum(BALANCE_AMT) AS TOTAL_BALANCE_AMT
FROM PPC_MAST_201312
GROUP BY BALANCE_SHEET
ORDER BY BALANCE_SHEET ;
-- +---------------+-------------------+
-- | BALANCE_SHEET | TOTAL_BALANCE_AMT |
-- +---------------+-------------------+
-- | ASSET | 9907921 |
-- | LIABILITY | 5280659 |
-- +---------------+-------------------+
-- 2) 고객별 상품 갯수 테이블 생성
SELECT
SSN,
COUNT(ACCT_NO) AS PPC
FROM PPC_MAST_201312
GROUP BY SSN
ORDER BY PPC DESC;
-- +---------------+-----+
-- | SSN | PPC |
-- +---------------+-----+
-- | 7802221111111 | 4 |
-- | 6711032222111 | 3 |
-- | 5605099999222 | 2 |
-- | 8910103333222 | 2 |
-- | 8307153333444 | 1 |
-- | 8012301111333 | 1 |
-- +---------------+-----+
-- 3) 고객별 상품 갯수가 3개 이상인 고객만 추출
SELECT
SSN,
COUNT(ACCT_NO) AS PPC
FROM PPC_MAST_201312
GROUP BY SSN
HAVING PPC >= 3
ORDER BY PPC DESC;
-- +---------------+-----+
-- | SSN | PPC |
-- +---------------+-----+
-- | 7802221111111 | 4 |
-- | 6711032222111 | 3 |
-- +---------------+-----+
-- 4) 고객별 총수익과 PPC 테이블
SELECT SSN,
COUNT(ACCT_NO) AS PPC,
SUM(PRFT) AS PRFT_TOTAL
FROM PPC_MAST_201312
GROUP BY SSN
ORDER BY PPC DESC;
-- +---------------+-----+------------+
-- | SSN | PPC | PRFT_TOTAL |
-- +---------------+-----+------------+
-- | 7802221111111 | 4 | 3673 |
-- | 6711032222111 | 3 | 15059 |
-- | 5605099999222 | 2 | 7213 |
-- | 8910103333222 | 2 | 473 |
-- | 8307153333444 | 1 | 585 |
-- | 8012301111333 | 1 | 780 |
-- +---------------+-----+------------+
Reference
- 칼퇴족 김대리는 알고 나만 모르는 SQL - 책밥
22 Jun 2023
1. 집계 데이터를 활용한 숫자 데이터
| 함수 |
설명 |
| COUNT |
COUNT() : NULL값을 포함한 행의 수 |
| |
COUNT(col_nm_) : NULL값을 제외한 행의 수 |
| |
COUNT(DISTINCT(col_nm_)) : NULL값을 제외한 행의 수 |
| SUM |
SUM(col_nm_) : 전체 합계 |
| AVG |
AVG(col_nm_) : 평균 |
| MAX |
MAX(col_nm_) : 최댓값 |
| MIN |
MIN(col_nm_) : 최솟값 |
| STDENV or STD |
STDENV(col_nm_) : (Orcle or Mysql)표준편차 |
| VARIANCE |
VARIANCE(col_nm_) : 분산 |
-- EX)
-- 1) 전체 행 카운트
SELECT COUNT(*) FROM PERF_MAST_201312;
-- +----------+
-- | COUNT(*) |
-- +----------+
-- | 11 |
-- +----------+
-- 2) TOT_AMT_1열의 NULL 제외 행 카운트
SELECT COUNT(TOT_AMT_1) FROM PERF_MAST_201312;
-- +------------------+
-- | COUNT(TOT_AMT_1) |
-- +------------------+
-- | 8 |
-- +------------------+
-- 3) SEG열의 유니크한 행값 카운트
SELECT COUNT(DISTINCT(SEG)) FROM PERF_MAST_201312;
-- +----------------------+
-- | COUNT(DISTINCT(SEG)) |
-- +----------------------+
-- | 2 |
-- +----------------------+
-- 4) TOT_AMT_1의 평균 계산 (Null 제외)
SELECT AVG(TOT_AMT_1) FROM PERF_MAST_201312;
-- +----------------+
-- | AVG(TOT_AMT_1) |
-- +----------------+
-- | 1496144.7500 |
-- +----------------+
-- 5) TOT_AMT_1의 평균 계산 (Null은 0으로 치환)
SELECT AVG(COALESCE(TOT_AMT_1, 0)) AS AVG_TOTAL_AMT
FROM PERF_MAST_201312;
-- +----------------------+
-- | COUNT(DISTINCT(SEG)) |
-- +----------------------+
-- | 2 |
-- +----------------------+
-- 6) TOT_AMT_1열의 표준편차 계산
SELECT STD(TOT_AMT_1) FROM PERF_MAST_201312;
-- +--------------------+
-- | STD(TOT_AMT_1) |
-- +--------------------+
-- | 3173918.0474547837 |
-- +--------------------+
- 집계 함수 사용시, NULL은 자동으로 계산에서 무시된다.
- 때문에, 0값을 평균에 포함하고 싶을 경우,
COALESCE함수를 사용하여 0으로 치환한 후 AVG계산이 필요하다.
2. 조건문 이해하기
1) CASE WHEN
: python은 if / elif / else문과 같은 역할을 하며,
집계함수인 GROUP BY절과 결합하여 자주 사용된다.
SELECT col_nm_1,
CASE WHEN [conditioanl_1] THEN [output_1]
WHEN [conditioanl_2] THEN [output_2]
ELSE [output_3] END AS new_col_nm
FROM 테이블명;
-- EX)
-- 1) SEG 기준 PB는 30%인상, MASS는 50%인하된 구매금액(TOT_AMT_1) 테이블 계산
SELECT PARTY_NM, SEG, TOT_AMT_1,
CASE WHEN SEG = 'PB' THEN TOT_AMT_1*1.3
WHEN SEG = 'MASS' THEN TOT_AMT_1*0.5
ELSE TOT_AMT_1 END AS TOT_AMT_1_FIX
FROM PERF_MAST_201312;
-- +-----------+------+-----------+---------------+
-- | PARTY_NM | SEG | TOT_AMT_1 | TOT_AMT_1_FIX |
-- +-----------+------+-----------+---------------+
-- | 임하영 | PB | 1000400 | 1300520.0 |
-- | 김아름 | PB | 790 | 1027.0 |
-- | 김지철 | PB | NULL | NULL |
-- | 김지연 | MASS | 254860 | 127430.0 |
-- | 윤일상 | MASS | 88805 | 44402.5 |
-- | 홍지은 | MASS | NULL | NULL |
-- | 이선우 | MASS | 90700 | 45350.0 |
-- | 최혜연 | MASS | NULL | NULL |
-- | 이동건 | PB | 9846000 | 12799800.0 |
-- | 강성범 | MASS | 540 | 270.0 |
-- | 김지현 | PB | 687063 | 893181.9 |
-- +-----------+------+-----------+---------------+
-- 2) 구매금액(TOT_AMT_1)에 대하여, SEG 기준 PB는 30%인상, MASS는 50%인하하며,
-- TOT_AMT_1이 TOT_AMT_2보다 같거나 클 경우, 추가 20% 인상하며, 작을 경우 20%인하하라
SELECT PARTY_NM, SEG, TOT_AMT_2, TOT_AMT_1,
CASE WHEN SEG = 'PB' AND TOT_AMT_1 >= TOT_AMT_2 THEN TOT_AMT_1 * 1.3 * 1.2
WHEN SEG = 'PB' AND TOT_AMT_1 < TOT_AMT_2 THEN TOT_AMT_1 * 1.3 * 0.8
WHEN SEG = 'MASS' AND TOT_AMT_1 >= TOT_AMT_2 THEN TOT_AMT_1 *0.5 * 1.2
WHEN SEG = 'MASS' AND TOT_AMT_1 < TOT_AMT_2 THEN TOT_AMT_1 *0.5 * 0.8
ELSE TOT_AMT_1 END AS TOT_AMT_1_FIX
FROM PERF_MAST_201312;
-- +-----------+------+-----------+-----------+---------------+
-- | PARTY_NM | SEG | TOT_AMT_2 | TOT_AMT_1 | TOT_AMT_1_FIX |
-- +-----------+------+-----------+-----------+---------------+
-- | 임하영 | PB | 788000 | 1000400 | 1560624.00 |
-- | 김아름 | PB | 1770 | 790 | 821.60 |
-- | 김지철 | PB | NULL | NULL | NULL |
-- | 김지연 | MASS | 578321 | 254860 | 101944.00 |
-- | 윤일상 | MASS | 659860 | 88805 | 35522.00 |
-- | 홍지은 | MASS | NULL | NULL | NULL |
-- | 이선우 | MASS | 5789 | 90700 | 54420.00 |
-- | 최혜연 | MASS | 6000 | NULL | NULL |
-- | 이동건 | PB | 5708900 | 9846000 | 15359760.00 |
-- | 강성범 | MASS | NULL | 540 | 540 |
-- | 김지현 | PB | 870000 | 687063 | 714545.52 |
-- +-----------+------+-----------+-----------+---------------+
--3) 2)번과 같이 매출이 발생할 때, 총 매출액을 계산하라.
SELECT SUM(CASE WHEN SEG = 'PB' AND TOT_AMT_1 >= TOT_AMT_2 THEN TOT_AMT_1 * 1.3 * 1.2
WHEN SEG = 'PB' AND TOT_AMT_1 < TOT_AMT_2 THEN TOT_AMT_1 * 1.3 * 0.8
WHEN SEG = 'MASS' AND TOT_AMT_1 >= TOT_AMT_2 THEN TOT_AMT_1 *0.5 * 1.2
WHEN SEG = 'MASS' AND TOT_AMT_1 < TOT_AMT_2 THEN TOT_AMT_1 *0.5 * 0.8
ELSE TOT_AMT_1 END) AS TOT_AMT_1_FIX_SUM
FROM PERF_MAST_201312;
-- +-------------------+
-- | TOT_AMT_1_FIX_SUM |
-- +-------------------+
-- | 17828177.12 |
-- +-------------------+
-- 이때 SELECT문 내, SUM 쿼리 앞에 다른 컬럼들을 입력하게 되면,
-- 다른 컬럼들은 Aggregation이 되지 않아 N개의 행을 갖는 반면,
-- SUM쿼리는 Aggregate되어 하나의 단일값을 갖기에,
-- 서로 나타내려는 행의 개수가 달라 에러가 발생하니 주의할 것.
2) DECODE(Oracle) 한정
: CASE WHEN 함수에서 조건문이 EQUAL 조건만 있는 경우, DECODE 함수를 사용할 수 도 있다.
-- 함수 문법
DECODE(col_nm, conditioanl_1, output_1,
conditioanl_2, output_2,
conditioanl_3, output_3, default_output) new_col_nm
SELECT PARTY_NM, SEG, TOT_AMT_1
DECODE( SEG , 'PB' , TOT_AMT_1*1.3,
, 'MASS' , TOT_AMT_1*0.5,
TOT_AMT_1) AS TOT_AMT_1_FIX
FROM PERF_MAST_201312;
+ 연습문제
- CASA_201312 테이블
+---------+----------+----------------+----------------+
| CUST_ID | CUST_SEG | BALANCE_201311 | BALANCE_201312 |
+---------+----------+----------------+----------------+
| 12344 | SILVER | 210000 | 200000 |
| 12478 | DIAMOND | 6015000 | 5800000 |
| 23458 | DIAMOND | 5000000 | 6200000 |
| 32134 | GOLD | 2354000 | 3200000 |
| 34181 | GOLD | 4200000 | 4100000 |
| 54560 | SILVER | 1000000 | 2000000 |
| 54789 | SILVER | 200000 | 300000 |
| 68477 | GOLD | 112000 | 3500 |
| 96147 | SILVER | 300000 | 1000010 |
+---------+----------+----------------+----------------+
-- 1) 12월 잔액이 11월 잔액보다 10% 증가시, 오퍼 제공 (단 등급별 차등 제공)
-- DIAMOND : 5,000 / GOLD : 3,000 / SILVER : 2,000원씩
-- 이때, 오퍼를 받게 될 고객들은 1, 그 외는 0으로 표기하는 OFFER_ACCEPT 컬럼 생성
SELECT *,
CASE WHEN BALANCE_201312 > BALANCE_201311 *1.1 THEN 1
ELSE 0
END AS OFFER_ACCEPT
FROM CASA_201312;
-- +---------+----------+----------------+----------------+--------------+
-- | CUST_ID | CUST_SEG | BALANCE_201311 | BALANCE_201312 | OFFER_ACCEPT |
-- +---------+----------+----------------+----------------+--------------+
-- | 12344 | SILVER | 210000 | 200000 | 0 |
-- | 12478 | DIAMOND | 6015000 | 5800000 | 0 |
-- | 23458 | DIAMOND | 5000000 | 6200000 | 1 |
-- | 32134 | GOLD | 2354000 | 3200000 | 1 |
-- | 34181 | GOLD | 4200000 | 4100000 | 0 |
-- | 54560 | SILVER | 1000000 | 2000000 | 1 |
-- | 54789 | SILVER | 200000 | 300000 | 1 |
-- | 68477 | GOLD | 112000 | 3500 | 0 |
-- | 96147 | SILVER | 300000 | 1000010 | 1 |
-- +---------+----------+----------------+----------------+--------------+
-- 2) 캠페인 대상수 / 오퍼수 / 반응률 각각을 계산 (*OFFER 대상이 되는 사람의 비율 계산)
SELECT
COUNT(*)
AS TOTAL_CNT,
SUM(
CASE WHEN BALANCE_201312 > BALANCE_201311 *1.1 THEN 1
ELSE 0 END
) AS OFFER_ACCEPT_CNT,
AVG(
CASE WHEN BALANCE_201312 > BALANCE_201311 *1.1 THEN 1
ELSE 0 END
) AS OFFER_RATE
FROM CASA_201312;
-- +-----------+------------------+------------+
-- | TOTAL_CNT | OFFER_ACCEPT_CNT | OFFER_RATE |
-- +-----------+------------------+------------+
-- | 9 | 5 | 0.5556 |
-- +-----------+------------------+------------+
-- 3) 수익(REVENUE) 계산 : 11, 12년도 잔액 합계, 12년도 평균수익 증가분, 평균수익(= 증가분의 0.9%)
SELECT
SUM(BALANCE_201311) AS BAL_1311,
SUM(BALANCE_201312) AS BAL_1312,
SUM(BALANCE_201312) - SUM(BALANCE_201311) AS INC_BAL,
ROUND((SUM(BALANCE_201312) - SUM(BALANCE_201311)) * 0.009) AS REV
FROM CASA_201312;
-- +----------+----------+---------+-------+
-- | BAL_1311 | BAL_1312 | INC_BAL | REV |
-- +----------+----------+---------+-------+
-- | 19391000 | 22803510 | 3412510 | 30713 |
-- +----------+----------+---------+-------+
-- 4) 비용(COST) 계산 : LMS(모두에게 30원씩), OFFER 비용 (등급별)
SELECT
COUNT(*) * 30 AS LMS_COST,
SUM(
CASE WHEN CUST_SEG = 'DIAMOND' AND BALANCE_201312 > BALANCE_201311 *1.1 THEN 5000
WHEN CUST_SEG = 'GOLD' AND BALANCE_201312 > BALANCE_201311 *1.1 THEN 3000
WHEN CUST_SEG = 'SILVER' AND BALANCE_201312 > BALANCE_201311 *1.1 THEN 2000
ELSE 0 END
) AS OFFER_COST
FROM CASA_201312;
-- +----------+------------+
-- | LMS_COST | OFFER_COST |
-- +----------+------------+
-- | 270 | 14000 |
-- +----------+------------+
Reference
- 칼퇴족 김대리는 알고 나만 모르는 SQL - 책밥
15 Jun 2023
Prep - Datetime
: 태블로 날짜 기반 함수
1) DATEDIFF
: 주어진 날짜 사이의 값을 반환 (뒷 날짜 - 앞 날짜)
DATEDIFF('month', #2023-01-07, #2023-03-01)
-- 2
DATEDIFF('month', #2023-08-07, #2023-03-023)
-- -5
2) DATEPART
: 주어진 날짜를 정수로 반환
DATEPART('month', #2023-01-07)
-- 01
DATEPART('day', #2023-01-07)
-- 07
6. 조건문을 활용한 필터링 방법
: 테블루에서 필터값을 많이 만들면, 연산 과정이 무거워지기에, 조건문을 활용하여 필터를 적용한 것과 같은 변수를 생성 - 1) 선택하고자 하는 일자만 True값을 반환하는 변수 생성
```sql
-- 선택한 월/일
DATEDIFF('month', [Order Date], [p.Date]) = 0
AND
DATEPART('day', [Order Date]) <= DATEPART('day', [p.Date])
```
- 2) True로 선택된 일자의 Profit만 출력하는 변수 생성
-- 선택한 월/일변수를 활용하여
IIF([선택한_월_TF], [Profit], NULL)
Vis
1. 동일 차트내 그래프 결합
Reference