별모양 스키마(Star Schema)

Kyle·2025년 9월 25일

“별모양 스키마(Star Schema)”는 데이터 웨어하우스(DW) 설계에서 가장 기본적이면서도 많이 쓰이는 모델이다.


1. 별모양 스키마(Star Schema)란?

  • 사실 테이블(Fact Table): 비즈니스 이벤트(매출, 주문, 클릭 등)를 담고 있고, 주로 숫자형 데이터(금액, 수량, 매출액 등)를 저장
  • 차원 테이블(Dimension Table): 사실 데이터를 설명하는 맥락을 제공 (예: 고객 정보, 제품 정보, 날짜, 지역 등)
  • 별 모양: 사실 테이블이 가운데 있고, 여러 차원 테이블이 방사형으로 연결되어 있기 때문에 “Star Schema”라고 함

예시 (온라인 쇼핑몰 매출 DW):

FactSales (사실 테이블)
 ├── CustomerDimension (차원: 고객)
 ├── ProductDimension (차원: 상품)
 ├── DateDimension (차원: 날짜)
 └── StoreDimension (차원: 매장)

2. Django 모델로 구현해보기

예를 들어, 매출 데이터 웨어하우스를 만든다고 가정

from django.db import models

# 차원 테이블들
class CustomerDimension(models.Model):
    customer_key = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)
    email = models.EmailField()
    region = models.CharField(max_length=100)

    def __str__(self):
        return f"{self.name} ({self.region})"

class ProductDimension(models.Model):
    product_key = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)
    category = models.CharField(max_length=100)
    price = models.DecimalField(max_digits=10, decimal_places=2)

    def __str__(self):
        return f"{self.name} - {self.category}"

class DateDimension(models.Model):
    date_key = models.DateField(primary_key=True)
    year = models.IntegerField()
    quarter = models.IntegerField()
    month = models.IntegerField()
    day = models.IntegerField()
    weekday = models.CharField(max_length=10)

    def __str__(self):
        return str(self.date_key)

class StoreDimension(models.Model):
    store_key = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)
    city = models.CharField(max_length=100)
    country = models.CharField(max_length=100)

    def __str__(self):
        return f"{self.name} ({self.city})"

# 사실 테이블
class SalesFact(models.Model):
    sale_id = models.AutoField(primary_key=True)
    customer = models.ForeignKey(CustomerDimension, on_delete=models.CASCADE)
    product = models.ForeignKey(ProductDimension, on_delete=models.CASCADE)
    date = models.ForeignKey(DateDimension, on_delete=models.CASCADE)
    store = models.ForeignKey(StoreDimension, on_delete=models.CASCADE)

    quantity = models.IntegerField()
    total_amount = models.DecimalField(max_digits=12, decimal_places=2)

    def __str__(self):
        return f"Sale {self.sale_id} - {self.total_amount}"

3. Django ORM에서 활용 예시

  • 매출 집계:
    from django.db.models import Sum
    
    # 2025년 매출 총합
    total_sales_2025 = SalesFact.objects.filter(
        date__year=2025
    ).aggregate(Sum("total_amount"))
  • 고객별 매출 TOP 5:
    top_customers = (
        SalesFact.objects
        .values("customer__name")
        .annotate(total=Sum("total_amount"))
        .order_by("-total")[:5]
    )
  • 카테고리별 월 매출:
    monthly_sales = (
        SalesFact.objects
        .values("date__year", "date__month", "product__category")
        .annotate(total=Sum("total_amount"))
        .order_by("date__year", "date__month")
    )

4. 왜 Star Schema가 중요한가?

  • 단순함: 구조가 직관적이어서 SQL/Django ORM 쿼리 작성이 쉬움
  • 성능 최적화: 조회 패턴에 맞춰 인덱싱을 잘 하면 BI 도구와 연동하기 좋음
  • 확장성: 새로운 차원(예: “배송 차원”)을 붙이기 쉬움

별모양 스키마 심화 이론

1. 기본 개념

  • 사실 테이블 (Fact Table)
    • 수치 데이터를 저장하는 테이블 (매출액, 수량, 비용 등).
    • 항상 외래키(FK) 로 차원 테이블을 참조
    • 보통 행 수가 수백만, 수억 건 이상 될 수 있음 → 성능 고려 필요.
    • 종류:
      • Transaction Fact: 주문, 결제처럼 이벤트 기반.
      • Periodic Snapshot Fact: 매일/매월 상태(잔액, 재고)를 기록.
      • Accumulating Snapshot Fact: 상태 변화 과정 (예: 배송 주문 진행상황).
  • 차원 테이블 (Dimension Table)
    • 사실 데이터를 설명하는 메타데이터 저장. (고객, 상품, 지역, 날짜 등)
    • 일반적으로 비교적 작음(수천~수십만 건).
    • 속성이 자주 변동됨 → SCD (Slowly Changing Dimension) 문제 발생.

2. 차원 관리 (Slowly Changing Dimension, SCD)

  • SCD Type 1 (덮어쓰기)
    • 고객의 지역이 바뀌면 그냥 업데이트해버림.
    • 과거 분석에는 문제가 생길 수 있음 (예: 2023년엔 서울 고객이었는데, 2025년에 부산으로 바뀌면 2023년 매출도 부산으로 계산됨).
  • SCD Type 2 (이력 관리)
    • 변경될 때 새로운 row를 삽입, start_date, end_date, is_current 같은 컬럼으로 관리.
    • 과거 시점 기준 분석 가능.
    • 가장 많이 쓰임.
  • SCD Type 3 (부분 이력)
    • 과거/현재 두 값만 저장. 잘 안 쓰임.

3. Star Schema vs Snowflake Schema

  • Star Schema (별모양): 차원 테이블이 정규화되지 않고 단순함. 쿼리 직관적. BI에 적합.
  • Snowflake Schema (눈송이): 차원 테이블이 정규화되어 더 분리됨. 저장공간 최적화, 하지만 쿼리 복잡.
  • 현업에서는 Star → Snowflake 혼합을 많이 씀.

4. 성능 최적화 포인트

  • 사실 테이블의 외래키 + 측정값(Measure) 조합에 인덱스 걸기.
  • 날짜 차원(Date Dimension)을 반드시 두는 게 중요 (쿼리 단순화 + 효율).
  • OLAP 도구/BI (예: PowerBI, Tableau, Superset)와 연동 고려.
  • Django ORM만으로도 충분히 쿼리 가능하지만, 대규모 DW → Spark / BigQuery / Redshift 같은 외부 시스템 연동 필요.

Django 실습 적용

1. SCD Type 2를 Django 모델로 구현

예: 고객 지역 변동 이력 관리

from django.db import models
from datetime import date

class CustomerDimension(models.Model):
    customer_key = models.AutoField(primary_key=True)
    customer_id = models.IntegerField()  # 실제 고객 ID
    name = models.CharField(max_length=255)
    email = models.EmailField()
    region = models.CharField(max_length=100)

    # SCD 관리 필드
    start_date = models.DateField(default=date.today)
    end_date = models.DateField(null=True, blank=True)
    is_current = models.BooleanField(default=True)

    def __str__(self):
        return f"{self.name} ({self.region})"

로직 예시 (고객 지역 업데이트 시):

def update_customer_region(customer_id, new_region):
    current_record = CustomerDimension.objects.get(customer_id=customer_id, is_current=True)
    
    if current_record.region != new_region:
        # 기존 레코드 종료
        current_record.end_date = date.today()
        current_record.is_current = False
        current_record.save()

        # 새 레코드 추가
        CustomerDimension.objects.create(
            customer_id=customer_id,
            name=current_record.name,
            email=current_record.email,
            region=new_region,
            start_date=date.today(),
            is_current=True
        )

실무 적용

Fack1, FackN, Fact 1 + application, Fact N + application

확장성 → 파티션을 어떤기준으로 쪼개느냐

  • 파티션 ( 테이블로 나눌수도있고 , DB로 나눌수있다 )
    • 기간, 회사, 서비스
    • 테이블 데이터
  • 파티션의 중요성
  • 테이블 데이터는 batch 로 create_or_update할 예정임

Fact1 으로 의사결정, (파티션은 기간[년도])

  • 디멘션 참조
    • 회사
    • 각 evidence
    • 회사 계정과목
  • 팩트
    • 원화금액
    • 외화코드
    • 외화금액
    • 증빙분류
    • 거래일자
      • 별도 “month” 필드 추가
        • 확인) datefield 에 month로 추가조회해야하는데 그걸 바꿔서 scan하는 작업을 DB로 해야함
    • 월(month)

계정과목 3depth 기준, 2deplth에 대한 집계는 appliaction 으로 집계

postgresql partitioning

5.12. Table Partitioning

profile
깔끔하게 코딩하고싶어요

0개의 댓글