본문 바로가기
C.W.K.
Stream
Lesson 03 of 05 · published

Slowly Changing Dimension — Type 1 vs Type 2

~12 min · modeling, scd, history

Level 0구경꾼
0 XP0/47 lessons0/11 achievements
0/120 XP to next level120 XP to go0% complete

Dimension 은 변한다 — 그러면 뭘 하지?

고객이 나라를 옮기고, 제품이 카테고리를 갈아타고, tier 배정이 오르내려. Dimension 의 속성은 시간이 가면 변하고, 그 순간 "이 주문이 났을 때 그 사람 tier 는 뭐였지?" 라는 질문이 생각보다 흥미로워져. Kimball 의 답이 slowly changing dimension (SCD) type 패밀리야. 그중 둘 — Type 1 과 Type 2 — 로 실전의 거의 전부가 덮여.

SCD Type 1 — 덮어쓰기

Dimension 이 항상 현재 상태만 비춰. 고객이 'KR' 에서 'US' 로 이사하면 country column 을 덮어써. 과거의 fact 들은 이제 처음부터 US 고객이었던 것처럼 보여. 단순하고, 저장 공간이 적게 들고, 이력은 없어.

쓸 때: 역사적 속성 값이 중요하지 않을 때 (오타 수정, 프로필 사진 교체, 입력 에러 정정). Dimension 에 대해 "as-of" 리포팅을 물을 일이 없을 때.

SCD Type 2 — 버전을 남기는 이력

추적하는 속성이 변할 때마다 dimension 테이블에 새 row 를 만들어 — effective-from / effective-to 타임스탬프와 current-flag 를 달아서. Fact 테이블은 fact 의 타임스탬프가 들어가는 validity window 를 가진 dimension row 와 join 하고, 그래서 역사 리포트가 고객의 그때 그 시점 속성을 보여 줘.

쓸 때: 역사가 중요할 때. 국가별 매출이라면 과거 주문은 고객이 지금 사는 곳이 아니라 그때 실제로 살던 나라에 잡혀야 하니까.

Code

같은 고객 변경, SCD Type 1 vs Type 2 표현·sql
-- Type 1: 그냥 덮어쓰기
UPDATE dim_customers
SET    country = 'US'
WHERE  customer_id = 'C100';
-- C100 의 과거 주문이 이제 US 주문처럼 보임. 이력 사라짐.

-- Type 2: 옛 row 닫고 새 row 열기
UPDATE dim_customers_v2
SET    valid_to = NOW(), is_current = FALSE
WHERE  customer_id = 'C100' AND is_current = TRUE;

INSERT INTO dim_customers_v2 (customer_id, country, tier, valid_from, valid_to, is_current)
VALUES ('C100', 'US', 'gold', NOW(), '9999-12-31', TRUE);

-- Type 2 schema:
CREATE TABLE dim_customers_v2 (
    customer_key   INTEGER PRIMARY KEY,        -- surrogate, 변경 시마다 새 거
    customer_id    TEXT NOT NULL,              -- 자연, row 간 반복
    name           TEXT,
    country        TEXT,
    tier           TEXT,
    valid_from     TIMESTAMP NOT NULL,
    valid_to       TIMESTAMP NOT NULL DEFAULT '9999-12-31',
    is_current     BOOLEAN NOT NULL DEFAULT TRUE
);
역사 속 옳은 시점에 fact 와 Type 2 dimension join·sql
-- *역사적* 국가별 매출 — 주문 *났을 때* 고객이 어느 나라
SELECT d.country,
       SUM(f.amount_usd) AS revenue
FROM   fact_orders f
JOIN   dim_customers_v2 d
  ON   f.customer_id = d.customer_id
  AND  f.order_date >= d.valid_from
  AND  f.order_date <  d.valid_to
GROUP BY d.country
ORDER BY revenue DESC;

External links

Exercise

본인이 작업한 dimension 하나 골라. 속성 5개를 나열하고, 하나마다 Type 1 인지 Type 2 인지 정한 다음 한 문장씩 이유를 달아. 기준이 거의 언제나 하나로 수렴한다는 걸 보게 될 거야 — "stakeholder 가 이 속성에 대해 as-of 리포트를 묻는 날이 오는가?"

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.