팩트 테이블과 차원 테이블
데이터 웨어하우스를 구성하는 기본 요소입니다
팩트 테이블과 차원 테이블은(는) CoddyKit의 무료 SQL Academy 강의입니다. 이것은 4개 중 2번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 SQL Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. SQL Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
데이터 웨어하우스란 무엇인가
데이터 웨어하우스는 보고서 작성과 분석 쿼리를 위해 설계된 중앙 저장소입니다. 빠른 쓰기에 최적화된 트랜잭션 데이터베이스와 달리, 데이터 웨어하우스는 대용량의 과거 데이터 전반에서 빠르게 읽을 수 있도록 조정되어 있습니다.
웨어하우스를 구성하는 가장 일반적인 방법은 스타 스키마를 사용하는 것입니다. 스타 스키마는 데이터를 팩트 테이블과 차원 테이블이라는 두 가지 테이블 유형으로 나눕니다.
팩트 테이블이란
팩트 테이블은 측정 가능한 정량적 이벤트, 즉 분석하려는 대상을 저장합니다. 각 행은 판매, 웹 페이지 조회, 고객 지원 티켓과 같은 하나의 비즈니스 이벤트 발생을 나타냅니다.
팩트 테이블은 일반적으로 행이 많고 열이 적으며, 대부분의 열은 차원 테이블을 가리키는 외래 키이거나 quantity 또는 revenue와 같은 숫자 측정값입니다.
CREATE TABLE fact_sales (
sale_id SERIAL PRIMARY KEY,
date_key INT NOT NULL,
product_key INT NOT NULL,
customer_key INT NOT NULL,
store_key INT NOT NULL,
quantity INT NOT NULL,
unit_price NUMERIC(10, 2) NOT NULL,
total_amount NUMERIC(12, 2) NOT NULL
);차원 테이블이란
차원 테이블은 각 팩트에 맥락을 제공하는 설명 속성을 저장합니다. 예를 들어 제품 차원에는 이름, 범주, 브랜드가 포함되고, 날짜 차원에는 일, 월, 분기, 연도가 포함됩니다.
차원 테이블은 일반적으로 행 수는 적지만 설명 열은 많습니다. 차원 테이블은 대체 정수 키를 사용하여 팩트 테이블과 조인됩니다.
CREATE TABLE dim_product (
product_key SERIAL PRIMARY KEY,
product_name VARCHAR(200) NOT NULL,
category VARCHAR(100),
brand VARCHAR(100),
unit_cost NUMERIC(10, 2)
);
CREATE TABLE dim_customer (
customer_key SERIAL PRIMARY KEY,
full_name VARCHAR(200) NOT NULL,
email VARCHAR(200),
country VARCHAR(100),
segment VARCHAR(50)
);날짜 차원
날짜 차원은 모든 웨어하우스에서 가장 일반적인 차원입니다. 팩트 테이블에 원시 TIMESTAMP를 저장하는 대신, 미리 구축한 달력 테이블을 참조하는 정수 키를 저장합니다.
이렇게 하면 쿼리 실행 시 날짜 연산을 수행하지 않고도 회계 분기, 요일, 공휴일 여부 플래그 및 기타 달력 속성으로 필터링하거나 그룹화할 수 있습니다.
CREATE TABLE dim_date (
date_key INT PRIMARY KEY, -- e.g. 20240315
full_date DATE NOT NULL,
day_of_week VARCHAR(10),
day_of_month INT,
month_num INT,
month_name VARCHAR(20),
quarter INT,
year INT,
is_holiday BOOLEAN DEFAULT FALSE,
fiscal_quarter INT
);
-- Sample row
INSERT INTO dim_date VALUES
(20240315, '2024-03-15', 'Friday', 15, 3, 'March', 1, 2024, FALSE, 2);스타 스키마 패턴
팩트 테이블 하나를 가운데에 놓고 차원 테이블이 바깥쪽으로 뻗어 나가는 다이어그램을 그리면 별처럼 보입니다. 그래서 스타 스키마라는 이름이 붙었습니다.
팩트 테이블의 외래 키는 각 차원의 기본 키를 가리킵니다. 쿼리는 일반적으로 팩트 테이블을 하나 이상의 차원과 조인하여 원시 수치에 설명 맥락을 추가합니다.
-- Join fact to two dimensions to enrich a sales report
SELECT
dp.product_name,
dp.category,
SUM(fs.quantity) AS total_units_sold,
SUM(fs.total_amount) AS total_revenue
FROM fact_sales fs
JOIN dim_product dp ON dp.product_key = fs.product_key
JOIN dim_date dd ON dd.date_key = fs.date_key
WHERE dd.year = 2024
GROUP BY dp.product_name, dp.category
ORDER BY total_revenue DESC;대체 키와 자연 키 비교
차원 테이블은 비즈니스 의미와 무관하게 데이터베이스가 생성하는 인공적인 정수인 대체 키를 사용합니다. 자연 키(예: 제품 SKU 또는 고객 이메일)는 시간이 지나면서 변경될 수 있지만 대체 키는 변경되지 않습니다.
대체 키를 사용하면 상위 시스템의 변경으로부터 팩트 테이블을 보호할 수 있고, 문자열 비교보다 정수 비교가 저렴하므로 조인도 더 빠르게 수행할 수 있습니다.
-- Surrogate key approach: integer join is fast
SELECT fs.sale_id, dc.full_name, fs.total_amount
FROM fact_sales fs
JOIN dim_customer dc ON dc.customer_key = fs.customer_key
WHERE dc.country = 'Germany'
LIMIT 10;
-- Natural key approach (avoid in warehouses): slower string join
-- JOIN dim_customer dc ON dc.email = fs.customer_email세부 수준: 팩트 테이블의 상세 수준
팩트 테이블의 세부 수준은 한 행이 정확히 무엇을 나타내는지 설명합니다. 웨어하우스를 구축하기 전에 세부 수준을 선언해야 합니다. 예를 들어 판매 주문의 개별 제품 항목마다 한 행을 저장한다고 정의할 수 있습니다.
세부 수준을 명확히 정의하면 모호한 집계를 방지할 수 있습니다. 서로 다른 행이 서로 다른 이벤트를 나타낸다면 SUM과 COUNT 결과는 의미가 없어집니다.
-- Grain: one row per product per order line
-- Each row = one line item sold in one transaction
SELECT
sale_id,
date_key,
product_key,
quantity,
unit_price,
total_amount
FROM fact_sales
WHERE date_key = 20240315
ORDER BY sale_id;가산적, 반가산적, 비가산적 측정값
팩트는 집계 방법에 따라 세 가지 유형으로 나뉩니다.
- 가산적 — 모든 차원에 걸쳐 합산할 수 있습니다(예:
revenue,quantity). - 반가산적 — 일부 차원에서는 합산할 수 있지만 모든 차원에서는 합산할 수 없습니다(예: 계정
balance는 고객별로는 합산할 수 있지만 시간별로는 합산할 수 없습니다). - 비가산적 — 의미 있게 합산할 수 없습니다(예:
unit_price,ratio). 대신 AVG 또는 다른 집계 함수를 사용합니다.
SELECT
dd.month_name,
SUM(fs.total_amount) AS total_revenue, -- additive
AVG(fs.unit_price) AS avg_unit_price, -- non-additive: use AVG
SUM(fs.quantity) AS total_units -- additive
FROM fact_sales fs
JOIN dim_date dd ON dd.date_key = fs.date_key
WHERE dd.year = 2024
GROUP BY dd.month_name, dd.month_num
ORDER BY dd.month_num;느리게 변하는 차원(SCD 유형 1 및 2)
차원 속성은 시간이 지나면서 변경됩니다. 고객이 국가를 옮기거나 제품의 범주가 바뀌는 경우가 그 예입니다. 느리게 변하는 차원(SCD)은 이러한 변경을 처리합니다.
- 유형 1 — 기존 값을 덮어씁니다. 간단하지만 기록이 사라집니다.
- 유형 2 — 새로운 대체 키와 유효 날짜를 가진 새 행을 추가합니다. 전체 기록을 보존하므로 과거 팩트가 차원의 올바른 버전을 계속 가리킬 수 있습니다.
-- SCD Type 2: add a new version of the row
ALTER TABLE dim_customer ADD COLUMN valid_from DATE;
ALTER TABLE dim_customer ADD COLUMN valid_to DATE;
ALTER TABLE dim_customer ADD COLUMN is_current BOOLEAN DEFAULT TRUE;
-- Expire the old row
UPDATE dim_customer
SET is_current = FALSE,
valid_to = CURRENT_DATE - INTERVAL '1 day'
WHERE email = 'anna@example.com' AND is_current = TRUE;
-- Insert the updated version
INSERT INTO dim_customer (full_name, email, country, segment, valid_from, valid_to, is_current)
VALUES ('Anna Muller', 'anna@example.com', 'Austria', 'Premium', CURRENT_DATE, '9999-12-31', TRUE);퇴화 차원
때로는 차원 속성에 별도의 테이블이 필요하지 않습니다. 퇴화 차원은 대응하는 차원 테이블 없이 팩트 테이블에 직접 저장되는 차원 키입니다.
대표적인 예로 주문 번호, 청구서 번호, 티켓 ID가 있습니다. 이러한 값은 상세 수준으로 파고들 때 맥락을 제공하지만, 별도의 테이블에 저장할 만한 다른 설명 열은 없습니다.
-- order_number is a degenerate dimension:
-- it lives in the fact table, no dim_order table needed
CREATE TABLE fact_order_lines (
line_id SERIAL PRIMARY KEY,
order_number VARCHAR(20) NOT NULL, -- degenerate dimension
date_key INT NOT NULL,
product_key INT NOT NULL,
customer_key INT NOT NULL,
quantity INT NOT NULL,
line_total NUMERIC(12, 2) NOT NULL
);
SELECT order_number, SUM(line_total) AS order_total
FROM fact_order_lines
GROUP BY order_number
ORDER BY order_total DESC
LIMIT 5;전체 스타 스키마 쿼리
모든 요소를 함께 살펴보면, 일반적인 웨어하우스 쿼리는 팩트 테이블을 여러 차원과 조인하고, 차원 속성에 필터를 적용한 뒤, 팩트 테이블의 측정값을 집계합니다.
최적화 프로그램은 이러한 다중 조인을 효율적으로 처리할 수 있습니다. 팩트 테이블의 외래 키에 인덱스가 있고 차원 테이블은 비교적 작기 때문입니다.
SELECT
dd.year,
dd.quarter,
dp.category,
dc.country,
SUM(fs.quantity) AS units_sold,
SUM(fs.total_amount) AS revenue
FROM fact_sales fs
JOIN dim_date dd ON dd.date_key = fs.date_key
JOIN dim_product dp ON dp.product_key = fs.product_key
JOIN dim_customer dc ON dc.customer_key = fs.customer_key
WHERE dd.year IN (2023, 2024)
AND dp.category = 'Electronics'
GROUP BY dd.year, dd.quarter, dp.category, dc.country
ORDER BY dd.year, dd.quarter, revenue DESC;빠른 확인: 팩트와 차원 비교
스타 스키마에서 팩트 테이블과 차원 테이블이 어떻게 다른지 이해했는지 확인해 보세요.
수업 요약
이 수업에서는 데이터 웨어하우스 스타 스키마를 구성하는 핵심 요소를 배웠습니다.
- 팩트 테이블은 숫자 측정값과 외래 키를 포함하여 측정 가능한 이벤트(판매, 클릭, 트랜잭션)를 저장합니다.
- 차원 테이블은 대체 키를 사용하여 설명 맥락(누가, 무엇을, 어디서, 언제)을 제공합니다.
- 세부 수준은 하나의 팩트 행이 정확히 무엇을 나타내는지 정의하므로, 구축하기 전에 선언해야 합니다.
- 측정값은 가산적, 반가산적 또는 비가산적이며, 이에 따라 집계 방법이 결정됩니다.
- SCD 유형 2는 유효 날짜가 포함된 새 행을 추가하여 과거 차원 값을 보존합니다.
- 퇴화 차원은 설명할 추가 속성이 없을 때 팩트 테이블에 직접 저장됩니다.
팩트 테이블과 차원 테이블을 이해하는 것은 빠르고 확장 가능하며 분석 역량이 뛰어난 웨어하우스를 구축하는 기반입니다.
자주 묻는 질문
“팩트 테이블과 차원 테이블” 강의는 무료인가요?
네 — “팩트 테이블과 차원 테이블” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 SQL Academy 강의 전체를 잠금 해제할 수 있습니다. SQL Academy 강의에는 총 4개의 강의가 포함되어 있습니다.
“팩트 테이블과 차원 테이블”에서 뭘 배우나요?
데이터 웨어하우스를 구성하는 기본 요소입니다 브라우저에서 직접 실행하는 실습 코드로 SQL Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
SQL Academy을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 SQL Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 2번째 강의입니다.
“팩트 테이블과 차원 테이블” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 SQL Academy 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 SQL Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- OLTP와 OLAP 비교
- 팩트 테이블과 차원 테이블
- 스타 스키마와 스노플레이크 스키마
- 분석 쿼리 작성