DB/PostgreSQL
[PostgreSQL] CUBE문을 사용해 사용자 액션 집계하기
불곰1
2021. 2. 28. 18:51
먼저 사용자들의 액션 플래그를 집계하는 쿼리를 다음과 같다고 가정합니다.
WITH
action_flag AS (
SELECT
user_id,
SIGN(SUM(CASE WHEN action = 'purchase' THEN 1 ELSE 0 END)) AS has_purchase,
SIGN(SUM(CASE WHEN action = 'review' THEN 1 ELSE 0 END)) AS has_review,
SIGN(SUM(CASE WHEN action = 'favorite' THEN 1 ELSE 0 END)) AS has_favorite
FROM action_log
GROUP BY user_id
)
SELECT *
from action_flag
이러한 결과는 다음과 같습니다.
user_id | has_purchase | has_review | has_favorite |
1 | 1 | 1 | 1 |
2 | 1 | 0 | 1 |
3 | 1 | 0 | 0 |
4 | 0 | 0 | 1 |
위와 같은 결과를 가지고 만들 수 있는 액션 플래그에 속한 사용자의 수를 구하려 합니다. 즉 구하고자 하는 결과값은 아래와 같습니다.
has_purchase | has_review | has_favorite | user_cnt |
0 | 0 | 0 | ~~~ |
0 | 0 | 1 | ~~~ |
0 | 1 | 0 | ~~~ |
1 | 0 | 0 | ~~~ |
0 | 1 | 1 | ~~~ |
등등등 |
위와 같은 결과를 한 테이블로 추출하기 위해선 아래와 같은 SQL이 나옵니다.
WITH
action_flag AS (
SELECT
user_id,
SIGN(SUM(CASE WHEN action = 'purchase' THEN 1 ELSE 0 END)) AS has_purchase,
SIGN(SUM(CASE WHEN action = 'review' THEN 1 ELSE 0 END)) AS has_review,
SIGN(SUM(CASE WHEN action = 'favorite' THEN 1 ELSE 0 END)) AS has_favorite
FROM action_log
GROUP BY user_id
)
, action_venn_diagram AS (
-- 3개를 모두한 경우
SELECT has_purchase, has_review, has_favorite, COUNT(1) AS user_cnt
FROM action_flag
GROUP BY has_purchase, has_review, has_favorite
UNION ALL
--3개 중 2개만 한 경우
SELECT NULL AS has_purchase, has_review, has_favorite, COUNT(1) AS user_cnt
FROM action_flag
GROUP BY has_review, has_favorite
UNION ALL
SELECT has_purchase, NULL AS has_review, has_favorite, COUNT(1) AS user_cnt
FROM action_flag
GROUP BY has_purchase, has_favorite
UNION ALL
SELECT has_purchase,has_review, NULL AS has_favorite, COUNT(1) AS user_cnt
FROM action_flag
GROUP BY has_purchase, has_review
UNION ALL
-- 3개 중 1개만 한 경우
SELECT NULL AS has_purchase, NULL AS has_review, has_favorite, COUNT(1) AS user_cnt
FROM action_flag
GROUP BY has_favorite
UNION ALL
SELECT has_purchase, NULL AS has_review, NULL AS has_favorite, COUNT(1) AS user_cnt
FROM action_flag
GROUP BY has_purchase
UNION ALL
SELECT NULL AS has_purchase,has_review, NULL AS has_favorite, COUNT(1) AS user_cnt
FROM action_flag
GROUP BY has_review
UNION ALL
-- 액션과 상관없이 모든 사용자 집계
SELECT NULL AS has_purchase, NULL AS has_review, NULL AS has_favorite, COUNT(1) AS user_cnt
FROM action_flag
)
SELECT *
from action_venn_diagram
;
각 지표에 맞게 직접 조회한 다음, UNION ALL로 데이터를 결합하는 형식이라 성능이 좋지 않습니다. 이러한 문제를 CUBE 구문을 사용하면 쉽게 해결할 수 있습니다.
WITH
action_flag AS (
SELECT
user_id,
SIGN(SUM(CASE WHEN action = 'purchase' THEN 1 ELSE 0 END)) AS has_purchase,
SIGN(SUM(CASE WHEN action = 'review' THEN 1 ELSE 0 END)) AS has_review,
SIGN(SUM(CASE WHEN action = 'favorite' THEN 1 ELSE 0 END)) AS has_favorite
FROM action_log
GROUP BY user_id
)
, action_venn_diagram AS (
SELECT
has_purchase,
has_review,
has_favorite,
COUNT(1) AS user_cnt
FROM action_flag
GROUP BY CUBE(has_purchase, has_review, has_favorite)
)
SELECT *
from action_venn_diagram;
CUBE
CUBE구문은 grouping 에서 컬럼의 다차원 소계를 생성하는데 사용합니다. 위 CUBE문을 사용한 결과는 다음과 같습니다.
has_purchase | has_review | has_favorite | user_cnt |
0 | 0 | 0 | 12 |
0 | 0 | 1 | 2 |
0 | 0 | 5 | |
0 | 1 | 0 | 412 |
0 | 0 | 1 | |
1 | 0 | 0 | 24 |
중간에 값이 NULL인 레코드는 해당 액션의 동작유무를 모르는 경우입니다.
CUBE는 다음과 같이 사용할 수 있습니다.
SELECT C1, C2, C3, 집계함수(C4)
FROM TABLE_NAME
GROUP BY CUBE (C1, C2, C3);
-- 특정 컬럼 제외 가능
SELECT C1, C2, C3, 집계함수(C4)
FROM TABLE_NAME
GROUP BY C1 CUBE (C2, C3);
GROUP BY CUBE(C1,C2,C3) 는 다음과 같이8개가 발생함
GROUPING SETS (
(C1,C2,C3),
(C1,C2),
(C1,C3),
(C2,C3),
(C1),
(C2),
(C3),
()
)