sql, mysql의 roll up과 grouping으로 group by를 효과적으로 사용하기
roll up
- 테이블 통계를 사용할 때 보통 group by를 자주 사용한다. group by와 더불어 좀 더 풍부한 내용을 제공하는 roll up에 대해 알아보자.
CREATE TABLE students (
id INTEGER auto_increment PRIMARY KEY,
name TEXT NOT NULL,
gender TEXT NOT NULL,
city TEXT NOT NULL
);
INSERT INTO students (name, gender, city)
VALUES ('Ryan', 'M', 'seoul'), ('Joanna', 'F', 'busan'), ('lee', 'F', 'seoul'), ('choi', 'F', 'busan');
- 위 테이블 students는 gender와 city를 enum으로 한다. 더불어 group by로 자주 통계 결과를 출력한다고 가정하자. 이 경우 아래와 같이 쿼리를 작성할 것이다.
select gender, city, count(1)
from students
group by gender, city;
gender | city | count(1) |
---|---|---|
M | seoul | 1 |
F | busan | 2 |
F | seoul | 1 |
- 충분하게 만족스러운 결과를 제공한다. 하지만, 모든 데이터의 총합, 첫 번째 그룹핑 enum인 M과 F 각각의 총합이 필요할 수 있다. 이 경우 아래와 같이
roll up
을 사용하면 바로 문제가 해결된다.
select gender, city, count(1)
from students
group by gender, city WITH ROLLUP;
gender | city | count(1) |
---|---|---|
F | busan | 2 |
F | seoul | 1 |
F | NULL | 3 |
M | seoul | 1 |
M | NULL | 1 |
NULL | NULL | 4 |
- not null인 gender와 city 칼럼에서 null이란 값이 존재한다. 이 경우는 무엇을 의미하는가? 바로 각 enum에서의 총합을 의미한다.
- (F, NULL)은 F의 총합으로서 3을 나타낸다.
- (M, NULL)은 M의 총합으로서 1을 나타낸다.
- (NULL, NULL)의 경우 모든 총합 4를 의미한다.
- 다만 위 방식에는 두 가지 문제가 존재한다.
- 만약 값이 NULL 인 경우 어떻게 되는가?
- NULL이란 모호한 값 말고 다른 방식으로 총합이란 의미를 표현할 수는 없는가?
함수 grouping()
- 위의 문제는 grouping 함수를 통해 해소 가능하다.
- 아래 테이블 students는 city가 nullable이다.
CREATE TABLE students2 (
id INTEGER auto_increment PRIMARY KEY,
name TEXT NOT NULL,
gender TEXT NOT NULL,
city TEXT NULL -- NULL 이 허용된다.
);
-- choi와 Ryan의 city는 null 이다.
INSERT INTO students2 (name, gender, city)
VALUES ('Ryan', 'M', null), ('Joanna', 'F', 'busan'), ('lee', 'F', 'seoul'), ('choi', 'F', null);
- 상황 1. 값에 NULL이 있는 경우
- 순서에 따라 어떤 NULL이 총합으로서 NULL인지 예측 가능하다. 하지만 값으로서는 차이를 가지지 못한다.
- 아래 테이블을 보면 (M, NULL), (F, NULL)이 각 각 두 개가 있는 것을 확인할 수 있다.
select gender, city, count(1)
from students2
group by gender, city WITH ROLLUP;
gender | city | count(1) |
---|---|---|
F | NULL | 1 |
F | busan | 1 |
F | seoul | 1 |
F | NULL | 3 |
M | NULL | 1 |
M | NULL | 1 |
NULL | NULL | 4 |
- 상황 2. grouping()으로 NULL이 그룹핑 값인지 판별한다.
- grouping 함수의 결과가 1인 레코드는, NULL은 총합으로서 NULL임을 보여준다. 이를 통해 위에서 구분하지 못했던 NULL의 두 가지 의미를 구분할 수 있게 되었다.
select gender, city, grouping(gender), grouping(city), count(1)
from students2
group by gender, city WITH ROLLUP;
gender | city | grouping(gender) | grouping(city) | count(1) |
---|---|---|---|---|
F | NULL | 0 | 0 | 1 |
F | busan | 0 | 0 | 1 |
F | seoul | 0 | 0 | 1 |
F | NULL | 0 | 1 | 3 |
M | NULL | 0 | 0 | 1 |
M | NULL | 0 | 1 | 1 |
NULL | NULL | 1 | 1 | 4 |
- 상황 3. grouping()과 case-when을 활용하여, 예쁘게 작성해보자. 이 경우 ‘총합’이란 enum이 city나 gender에는 없어야 할테다.
select
case when grouping(gender) = 1 then '총합' else gender end "gender"
, case when grouping(city) = 1 then '총합' else city end "city"
, count(1)
from students2
group by gender, city WITH ROLLUP;
gender | city | count(1) |
---|---|---|
F | NULL | 1 |
F | busan | 1 |
F | seoul | 1 |
F | 총합 | 3 |
M | NULL | 1 |
M | 총합 | 1 |
총합 | 총합 | 4 |