디멘젼/팩트 구성에 대한 방법론 (SCD)
요구되는 분석 관점에 따라 보관 방식을 선택한다.
(현재값만 유지할지, 시점 이력을 보존할지, 제한된 이력만 남길지)
각각의 Type으로 구분.
Type4는 문헌에 따라 정의가 혼용되기도 한다. 여기서는 mini-dimension(킴볼 정의) 를 Type4로 두고, “현재+히스토리 테이블 분리” 패턴은 참고로 별도 표기함.
테이블 예시 시나리오
고객 C001의 세그먼트가 변경됨
2025-01-01 ~ 2025-02-14: SMB2025-02-15 ~: ENTFact(매출) 2건
2025-02-10 매출 100 (변경 전)2025-03-01 매출 200 (변경 후)단순히 덮어쓰는(overwrite) 방식. 이력 유지는 되지 않는다.
이러면 업데이트 될때마다 속성 기준으로 미리 만들어 둔 집계(aggregate), 물리화 결과(MV/큐브)는 재계산이 필요할 수 있음.
과거의 데이터도 완전히 바뀌어야 하는 경우에 사용.
예를 들어 항상 최신 속성으로만 봐야 하는 경우(회원 정보에 문제나 오타가 있어 정정을 해야 하는경우)
dim_customer (Type1)
| customer_id | segment |
|---|---|
| C001 | SMB |
변경 후(2025-02-15)
| customer_id | segment |
|---|---|
| C001 | ENT |
팩트는 C001 만 참조하면 되니 변함이 없음. 즉 기록에 대한 참조가 사라진다.
변경 발생 시 새로운 행을 추가하고 대체키(surrogate key)를 부여한다.
유효한 시작일/종료일과 current 플래그를 두는 구성이 흔함.
하나의 테이블로 이력과 시점 추적이 가능(정석적이라고 함)
대신 변경이 잦을 경우 로우가 계속해서 증가하고, ETL 단계에서 매핑이 필요함.
Fact 적재 시점에 해당 일자/시점에 유효한 차원 버전(SK)을 찾아서 FK로 저장
dim_customer (Type2)
| customer_sk | customer_id | segment | valid_from | valid_to | is_current |
|---|---|---|---|---|---|
| 101 | C001 | SMB | 2025-01-01 | 2025-02-14 | N |
| 102 | C001 | ENT | 2025-02-15 | 9999-12-31 | Y |
fact_sales
| sales_dt | amount | customer_sk |
|---|---|---|
| 2025-02-10 | 100 | 101 |
| 2025-03-01 | 200 | 102 |
이미 쌓인 과거 Fact는 일반적으로 업데이트하지 않고, 과거 SK를 계속 참조하는 형태
현재 값과 다른 컬럼에 이전값과 현재 값을 동시에 저장하는 것. 현재 값은 업데이트.
이걸 대체 현실(alternate reality)로 표현하기도 하며, 상대적으로 드물게 사용.
바로 직전 값과 현재 값만 비교해도 충분한 경우에 사용.
데이터가 커지진 않지만 직전 이력 이전의 이력은 동일하게 소실됨.
개인적으로 e-value가 필요한 실시간 마트성 데이터가 이런 류가 아닌가 싶음
dim_customer (Type3)
| customer_id | segment_current | segment_prev |
|---|---|---|
| C001 | SMB | (null) |
변경 후(2025-02-15)
| customer_id | segment_current | segment_prev |
|---|---|---|
| C001 | ENT | SMB |
변경이 잦은 속성 그룹은 mini-dimension으로 분리하는 방식.
차원폭증(rapidly changing monster dimension) 문제를 완화한다고 알려져 있음.
둘의 차이점은(여기서는 “mini-dimension” vs “history table 패턴”을 구분해서 적음)
mini-dimension의 경우
히스토리 테이블 패턴(참고)
조인 복잡도가 상승하고 etl도 복잡해진다(당연하지만)
나는 결제 내역과 결과를 분리할때 이런 방식을 사용했었다.
dim_customer_base
| customer_sk | customer_id | name |
|---|---|---|
| 501 | C001 | Alice |
dim_customer_profile (mini-dimension)
| profile_sk | segment |
|---|---|
| 9001 | SMB |
| 9002 | ENT |
fact_sales
| sales_dt | amount | customer_sk | profile_sk |
|---|---|---|---|
| 2025-02-10 | 100 | 501 | 9001 |
| 2025-03-01 | 200 | 501 | 9002 |
dim_customer_current
| customer_id | segment_current |
|---|---|
| C001 | ENT |
dim_customer_history
| customer_id | segment | valid_from | valid_to |
|---|---|---|---|
| C001 | SMB | 2025-01-01 | 2025-02-14 |
| C001 | ENT | 2025-02-15 | 9999-12-31 |
아래 5/6/7은 “과거 이력을 보존”하면서도 “현재 속성 기준으로 과거 사실을 재분류해서 보고 싶다” 요구를 지원하기 위한 하이브리드로 알려져 있음.
Type 4 + 1
mini-dimension을 쓰되, base dimension에 현재 mini-dimension 키를 Type1처럼 덮어써서 현재 속성 접근을 단순화하는 방식.
dim_customer_base (Type5 느낌)
| customer_sk | customer_id | name | current_profile_sk |
|---|---|---|---|
| 501 | C001 | Alice | 9002 |
Type 2 + 1 (+3로도 설명됨)
행 추가는 Type2처럼 하되, 차원에 현재값 컬럼(Type1) 을 두고, 그 현재값 컬럼은 동일 customer_id의 모든 버전 행에 대해 덮어쓴다.
과거 행에는 history 속성 제공하고 현재의 값도 같이 제공한다.
과거의 값과 현재값 대조/분석이 필요한 경우에 좋을듯 하다 → 추측임
dim_customer (Type6 예시)
| customer_sk | customer_id | segment_hist | segment_current |
|---|---|---|---|
| 101 | C001 | SMB | ENT |
| 102 | C001 | ENT | ENT |
팩트가 두 개의 FK를 가진다.
타입 6에서 디멘젼의 업데이트 부담을 덜 수 있다(차원 과거행에 current 컬럼을 덮어쓰는 부담 회피).
(참조하는 팩트 테이블에 적재 시점에 두 키를 함께 저장하는 것)
fact_sales (Type7 예시)
| sales_dt | amount | customer_sk | customer_id |
|---|---|---|---|
| 2025-02-10 | 100 | 101 | C001 |
| 2025-03-01 | 200 | 102 | C001 |
선택기준?