집약과 자르기
집약
집약 함수
- COUNT
- SUM
- AVG
- MAX
- MIN
여러 개의 레코드를 한 개의 레코드로 집약
- 한 사람과 관련된 정보가 여러 개의 레코드에 분산되어 있는 비집약 테이블을 집약 테이블로 만들 경우 다음과 같은 쿼리를 생각해볼 수 있음
SELECT id,
CASE WHEN data_type = 'A' THEN data_1 ELSE NULL END AS data_1
CASE WHEN data_type = 'A' THEN data_2 ELSE NULL END AS data_2
CASE WHEN data_type = 'B' THEN data_3 ELSE NULL END AS data_3
CASE WHEN data_type = 'B' THEN data_4 ELSE NULL END AS data_4
CASE WHEN data_type = 'B' THEN data_5 ELSE NULL END AS data_5
CASE WHEN data_type = 'C' THEN data_6 ELSE NULL END AS data_6
FROM NonAggTbl
GROUP BY id
- 그러나 위의 쿼리는 오류가 발생함
- GROUP BY 구로 집약했을 때 SELECT 구에는 아래의 세 가지만 입력할 수 있음
- 상수
- GROUP BY 구에서 사용한 집약 키
- 집약 함수
- 아래와 같이 집약 함수를 사용하여 작성해야 함
SELECT id,
MAX(CASE WHEN data_type = 'A' THEN data_1 ELSE NULL END) AS data_1
MAX(CASE WHEN data_type = 'A' THEN data_2 ELSE NULL END AS data_2)
MAX(CASE WHEN data_type = 'B' THEN data_3 ELSE NULL END AS data_3)
MAX(CASE WHEN data_type = 'B' THEN data_4 ELSE NULL END AS data_4)
MAX(CASE WHEN data_type = 'B' THEN data_5 ELSE NULL END AS data_5)
MAX(CASE WHEN data_type = 'C' THEN data_6 ELSE NULL END AS data_6)
FROM NonAggTbl
GROUP BY id
- 집약, 해시, 정렬
- GROUP BY에서는 정렬 또는 해시를 사용
- 정렬과 해시 모두 메모리를 많이 사용하므로 충분한 워킹 메모리가 확보되지 않으면 스왑이 발생하여 성능 저하 (TEMP 탈락)
합쳐서 하나
- 문제 1. 연령대 별로 가격이 다른 제품 중 0 ~ 100세가 이용 가능한 제품 찾기
- 상수를 합해 101인 제품 선택
SELECT product_id
FROM PriceByAge
GROUP BY product_id
HAVING SUM(high_age - low_age + 1) = 101
- 문제 2. 호텔 방마다 도착일과 출발일을 기록한 테이블을 보고 숙박한 날이 10일 이상인 방 찾기
SELECT room_nbr,
SUM(end_data - start_date) AS working_days
FROM HotelRooms
GROUP BY room_nbr
HAVING SUM(end_date - start_date) >= 10
자르기
자르기와 파티션
- GROUP BY 구는 집약 이외에도 자르기 기능을 가짐
- 문제 1. 이름 첫 글자를 사용해 특정한 알파벳으로 시작하는 이름을 가진 사람이 몇 명인지 집계
SELECT SUBSTRING(name, 1, 1) AS label,
COUNT(*)
FROM persons
GROUP BY SUBSTRING(name, 1, 1)
- GROUP BY 구로 잘라 만든 하나하나의 부분 집합을 수학적으로 파티션(partition)이라고 함
- 문제 2. 나이 기준으로 자르기
SELECT CASE WHEN age < 20 THEN '어린이'
WHEN age BETWEEN 20 AND 69 THEN '성인'
WHEN age >= 70 THEN '노인'
ELSE NULL END AS age_class,
COUNT(*)
FROM persons
GROUP BY CASE WHEN age <= 20 THEN '어린이'
WHEN age BETWEEN 20 AND 69 THEN '성인'
WHEN age >= 70 THEN '노인'
ELSE NULL END;
- GROUP BY 구에 CASE 식 또는 함수를 사용해도 실행 계획에 영향은 없음
- 그러나 단순한 필드가 아닌 연산을 추가한 식을 GROUP BY 구의 키로 할 경우 CPU 연산에 오버 헤드
PARTITION BY 구를 사용한 자르기
- 문제 1. 연령 등급 내에서 어린 순서로 순위 매기기
SELECT name,
age,
CASE WHEN age<20 THEN '어린이'
WHEN age BETWEEN 20 AND 69 THEN '성인'
WHEN age>=70 THEN '노인'
ELSE NULL END AS age_class,
RANK() OVER(PARTITION BY CASE WHEN age<20 THEN '어린이'
WHEN age BETWEEN 20 AND 69 THEN '성인'
WHEN age>=70 THEN '노인'
ELSE NULL END
ORDER BY age) AS age_rank_in_class
FROM Persons
ORDER BY age_class, age_rank_in_class)