2021-12-09 19:00 작성
My SQL basic 메모
Table of contents
ROLLUP 샘플 table 세팅하기
다음의 구문은 product lines와 years로 요약된 order 값들을 저장하는 sales
table을 생성한다. 데이터는 products
, orders
그리고 orderDetails
tables로부터 온다.
CREATE TABLE sales
SELECT
productLine,
YEAR(orderDate) orderYear,
SUM(quantityOrdered * priceEach) orderValue
FROM
orderdetails
INNER JOIN
orders USING (orderNumber)
INNER JOIN
products USING (productCode)
GROUP BY
productLine,
YEAR(orderDate);
SELECT * FROM sales;
output:
productLine | orderYear | orderValue |
---|---|---|
Classic Cars | 2003 | 1374832.22 |
Classic Cars | 2004 | 1763136.73 |
Classic Cars | 2005 | 715953.54 |
Motorcycles | 2003 | 348909.24 |
Motorcycles | 2004 | 527243.84 |
Motorcycles | 2005 | 245273.04 |
Planes | 2003 | 309784.20 |
Planes | 2004 | 471971.46 |
Planes | 2005 | 172881.88 |
Ships | 2003 | 222182.08 |
…
ROLLUP 개요
Grouping set은 그룹화하고자 하는 columns의 집합이다. 예를 들어, 다음의 쿼리는 productline
으로 grouping set을 생성한다.
SELECT
productline,
SUM(orderValue) totalOrderValue
FROM
sales
GROUP BY
productline;
output:
productline | totalOrderValue |
---|---|
Classic Cars | 3853922.49 |
Motorcycles | 1121426.12 |
Planes | 954637.54 |
Ships | 663998.34 |
Trains | 188532.92 |
Trucks and Buses | 1024113.57 |
Vintage Cars | 1797559.63 |
다음의 쿼리는 ()
를 통해 빈 grouping set을 생성한다:
SELECT
SUM(orderValue) totalOrderValue
FROM
sales;
output:
totalOrderValue |
---|
9604190.61 |
만약, 하나의 쿼리 안에서 두 개 혹은 그 이상의 groping sets를 만들어내고 싶다면 UNION ALL
연산자가 도움이 될 수 있다.
SELECT
productline,
SUM(orderValue) totalOrderValue
FROM
sales
GROUP BY
productline
UNION ALL
SELECT
NULL,
SUM(orderValue) totalOrderValue
FROM sales;
output: 조건이 서로 다른 그룹들을 묶어 결과를 반환한다.
productline | totalOrderValue |
---|---|
Classic Cars | 3853922.49 |
Motorcycles | 1121426.12 |
Planes | 954637.54 |
Ships | 663998.34 |
Trains | 188532.92 |
Trucks and Buses | 1024113.57 |
Vintage Cars | 1797559.63 |
NULL | 9604190.61 |
UNION ALL
에서 모든 쿼리는 columns의 수가 같아야 하기 때문에 위의 예시에서는 두 번째 쿼리에서 NULL
을 넣어 조건을 만족시켰다.
productLine
column의 NULL
은 totalOrderValue
의 전체 합계를 표시한다.
위의 쿼리는 productLine
별 totalOrderValue
을 보여줄 뿐만 아니라 총 합계도 보여주지만 두 가지 문제점이 산재한다.
- 쿼리가 길다.
- 데이터베이스는 내부적으로 두 가지 별개의 쿼리를 실행시킨 후에 하나로 결합하기 때문에 퍼포먼스 이슈가 생길 수 있다.
위의 문제점들을 고치기 위해 ROLLUP
을 사용할 수 있다.
ROLLUP
은 GROUP BY
절의 확장형으로 사용된다:
SELECT
select_list
FROM
table_name
GROUP BY
c1, c2, c3 WITH ROLLUP;
ROLLUP
은 GROUP BY
절에 명시된 columns나 expressions에 기반한 다수의 grouping sets를 만들어낸다. 예를 들어:
SELECT
productLine,
SUM(orderValue) totalOrderValue
FROM
sales
GROUP BY
productLine WITH ROLLUP;
output: 두 개의 쿼리를 실행하지 않고도 하나의 쿼리로 같은 결과값을 기대할 수 있다.
productLine | totalOrderValue |
---|---|
Classic Cars | 3853922.49 |
Motorcycles | 1121426.12 |
Planes | 954637.54 |
Ships | 663998.34 |
Trains | 188532.92 |
Trucks and Buses | 1024113.57 |
Vintage Cars | 1797559.63 |
NULL | 9604190.61 |
결과에서 보여진 것처럼 ROLLUP
은 소계(subtotals) 뿐만 아니라 총계를 계산한다. 만약, 복수의 columns가 GROUP BY
절에서 명시되었다면 ROLLUP
은 입력 columns 사이에서 계층을 추정한다.
예를 들어:
GROUP BY c1, c2, c3 WITH ROLLUP
ROLLUP
은 다음의 계층에 따라 추정한다:
c1 > c2 > c3
그리고 다음과 같은 grouping sets를 만든다:
(c1, c2, c3)
(c1, c2)
(c1)
()
GROUP BY
에서 명시된 두 개의 columns가 존재한다고 해보자:
GROUP BY c1, c2 WITH ROLLUP
그러면 ROLLUP
은 다음의 grouping sets을 만든다.
(c1, c2)
(c1)
()
다음의 예시 쿼리를 보자:
SELECT
productLine,
orderYear,
SUM(orderValue) totalOrderValue
FROM
sales
GROUP BY
productLine,
orderYear
WITH ROLLUP;
output:
ROLLUP
은 productLine
이 변경될 때 마다 소계를 만들며 끝에 총계를 만든다.
이 경우에 계층은 다음과 같다:
productLine > orderYear
만약 계층을 뒤집으면:
SELECT
orderYear,
productLine,
SUM(orderValue) totalOrderValue
FROM
sales
GROUP BY
orderYear,
productLine
WITH ROLLUP;
output:
ROLLUP
은 year
가 바뀔 때 마다 소계를 만들며 끝에 총계를 만든다.
예시의 계층은 다음과 같다:
orderYear > productLine
GROUPING() function
Result set의 NULL
이 소계와 총계를 나타내는지 여부를 확인하는데 있어 GROUPING()
function을 사용한다.
GROUPING()
function은 초집합체(super-aggregate) row에서 NULL
발생 시 1
을 반환하고 그렇지 않으면 0
을 반환한다.
GROUPING()
function은 SELECT
, HAVING
그리고 ORDER BY
(MySQL 8.0.12 버전)에서 사용할 수 있다.
다음 쿼리를 보자:
SELECT
orderYear,
productLine,
SUM(orderValue) totalOrderValue,
GROUPING(orderYear),
GROUPING(productLine)
FROM
sales
GROUP BY
orderYear,
productLine
WITH ROLLUP;
output:
GROUPING(orderYear)
은 초집합체 row의 orderYear
column에서 NULL
이 발생하면 1
을 반환하고 그렇지 않으면 0
을 반환한다.
비슷하게 GROUPING(prouductLine)
은 초집합체 row의 productLine
에서 NULL
이 발생할 때 1
을 반환한다. 그렇지 않으면 0
을 반환한다.
GROUPING()
function은 종종 초집합체 NULL
을 직접적으로 보여주기보다 의미있는 라벨로 교체하여 보여주기 위한 용도로 사용된다.
다음의 예시는 IF()
function이 GROUPING()
function과 함께 어떻게 orderYear
와 productLine
columns에서 NULL
을 의미있는 대체 라벨로 치환하는지 보여준다.
SELECT
IF(GROUPING(orderYear),
'All Years',
orderYear) orderYear,
IF(GROUPING(productLine),
'All Product Lines',
productLine) productLine,
SUM(orderValue) totalOrderValue
FROM
sales
GROUP BY
orderYear,
productLine
WITH ROLLUP;
output:
orderYear | productLine | totalOrderValue |
---|---|---|
2003 | Classic Cars | 1374832.22 |
2003 | Motorcycles | 348909.24 |
2003 | Planes | 309784.20 |
2003 | Ships | 222182.08 |
2003 | Trains | 65822.05 |
2003 | Trucks and Buses | 376657.12 |
2003 | Vintage Cars | 619161.48 |
2003 | All Product Lines | 3317348.39 |
2004 | Classic Cars | 1763136.73 |
2004 | Motorcycles | 527243.84 |
2004 | Planes | 471971.46 |
2004 | Ships | 337326.10 |
2004 | Trains | 96285.53 |
2004 | Trucks and Buses | 465390.00 |
2004 | Vintage Cars | 854551.85 |
2004 | All Product Lines | 4515905.51 |
2005 | Classic Cars | 715953.54 |
2005 | Motorcycles | 245273.04 |
2005 | Planes | 172881.88 |
2005 | Ships | 104490.16 |
2005 | Trains | 26425.34 |
2005 | Trucks and Buses | 182066.45 |
2005 | Vintage Cars | 323846.30 |
2005 | All Product Lines | 1770936.71 |
All Years | All Product Lines | 9604190.61 |
GROUPING(orderYear)
은 연도의 끝에서 GROUPING(productLine)
은 GROUP BY
의 orderYear
조건을 충족시키는 productLine
끝에서 NULL
이 발생하는 것을 알 수 있다.