Fact table vs dimension table: разница

Карьерник — квиз-тренажёр в Telegram с 1500+ вопросами для собесов аналитика. SQL, Python, A/B, метрики. Бесплатно.

Короткий ответ

  • Fact table (таблица фактов) — хранит события и измерения: что произошло, сколько, когда. Много строк.
  • Dimension table (таблица измерений) — хранит контекст: кто, что, где. Мало строк.
  • Они соединяются через FK: fact.dim_id = dim.id.

Классическая модель — star schema (звёздная схема): fact в центре, dimension вокруг.

Пример e-commerce

Fact table: orders

Хранит события заказов — что купили, сколько, когда.

order_id | user_id | product_id | store_id | date_id | qty | amount
---------|---------|------------|----------|---------|-----|-------
   1001  |   42    |    850     |    7     |  21201  |  2  | 1200
   1002  |   43    |    851     |    7     |  21201  |  1  |  500
  • Много строк (миллионы, миллиарды)
  • Чаще всего integer / numeric колонки
  • FK на dimension таблицы

Dimension table: users

Хранит характеристики пользователей.

id | name    | email            | signup_at  | country
---|---------|------------------|------------|--------
42 | Alice   | alice@example.com | 2024-05-01 | RU
43 | Bob     | bob@example.com   | 2025-02-15 | KZ
  • Меньше строк (тысячи / сотни тысяч)
  • Описательные данные

Dimension table: products

id  | name           | category   | price
----|----------------|------------|------
850 | iPhone 15      | Electronics| 80000
851 | AirPods Pro    | Electronics| 20000

Dimension table: date (date dim)

Стандарт — отдельная date dimension:

date_id  | date       | day | month | year | day_of_week | is_weekend
---------|------------|-----|-------|------|-------------|-----------
21201    | 2026-04-21 | 21  |   4   | 2026 | 2 (Tue)     | false

Удобно для анализа по календарю (какой день недели, праздник, квартал).

Главные отличия

Fact Dimension
Содержание События, факты Контекст, характеристики
Рост Постоянно растёт Медленно растёт
Строк миллионы+ тысячи-сотни тысяч
Типы колонок Числа, FK Текст, даты, категории
Grain (гранулярность) событие / транзакция объект (user, product)
FK наличие много FK обычно только PK
Обновления append-only редко

Типы fact-таблиц

Transaction fact

Одна строка = одно событие. Стандарт.

Пример: один заказ = одна строка.

Snapshot fact

Фотография состояния на момент времени.

Пример: баланс счёта на конец каждого дня.

Accumulating snapshot

Разные стадии процесса (заказ оформлен, отгружен, доставлен) — одна строка, но с несколькими timestamp.

Типы dimension-таблиц

Slowly Changing Dimension (SCD)

Как обрабатывать изменения в dimension данных:

  • SCD Type 1: обновление на месте (теряем историю)
  • SCD Type 2: новая строка с valid_from / valid_to (сохраняем историю)
  • SCD Type 3: отдельная колонка «prev_value» (частично сохраняем)

Пример SCD2 для users:

id | name  | country | valid_from  | valid_to    | is_current
---|-------|---------|-------------|-------------|-----------
42 | Alice | RU      | 2024-05-01  | 2025-08-01  | false
42 | Alice | KZ      | 2025-08-01  | NULL        | true

Alice переехала из RU в KZ.

Star schema vs snowflake schema

Star schema

Fact в центре, dimensions вокруг — все денормализованы.

        dim_users
             |
dim_date --- fact_orders --- dim_products
             |
        dim_stores
  • Плюсы: быстрые JOIN, простые запросы
  • Минусы: дубликаты данных (denormalized)

Snowflake schema

Dimensions нормализованы (у dim есть свои suba-dim):

dim_products → dim_category → dim_department
  • Плюсы: меньше дубликатов
  • Минусы: больше JOIN, сложнее

В DWH предпочитают star schema. Denormalization — не грех, а норма.

Проектирование

Шаг 1. Определить бизнес-процесс

«Заказы», «подписки», «клики».

Шаг 2. Определить grain

На каком уровне строка? Одна покупка? Один клик? Один день-пользователь?

Шаг 3. Определить dimensions

Кто, что, где, когда участвует? → dimension-таблицы.

Шаг 4. Определить facts

Что измеряем (количество, сумма, время)? → колонки в fact.

Шаг 5. Проверить

Можете ли ответить на ключевые бизнес-вопросы простыми SQL?

Пример запросов

Выручка по категориям за месяц

SELECT p.category, SUM(f.amount) AS revenue
FROM fact_orders f
JOIN dim_products p ON p.id = f.product_id
JOIN dim_date d ON d.date_id = f.date_id
WHERE d.year = 2026 AND d.month = 4
GROUP BY p.category;

Fact хранит amount и ссылки. Dimension даёт контекст (category, month).

Топ-10 продуктов

SELECT p.name, SUM(f.qty) AS total_sold
FROM fact_orders f
JOIN dim_products p ON p.id = f.product_id
GROUP BY p.name
ORDER BY total_sold DESC
LIMIT 10;

На собесе

Типичные вопросы:

«В чём разница fact и dimension?» Fact — события и измерения. Dimension — контекст.

«Star или snowflake?» В DWH — star. Denormalization оправдана скоростью запросов.

«Что такое SCD Type 2?» Сохранение истории изменений через valid_from/valid_to и is_current.

«Какая grain у fact table?» Уровень детализации. Один event = строка? Один день = строка?

Частые ошибки

Ошибка 1. Всё свалить в одну таблицу

Денормализованный «широкий» fact с 100 колонками — кошмар для чтения и записи.

Ошибка 2. Fact без FK

Если в fact хранить строки 'iPhone 15' вместо product_id — повторы, ошибки, сложнее обновить.

Ошибка 3. Dimension с мутирующими данными без SCD

Изменили category продукта → историческая аналитика сломалась.

Ошибка 4. Игнорировать date dimension

«У меня же date column в fact». Но нет контекста (is_weekend, holiday) → постоянно писать CASE.

Ошибка 5. Слишком мелкая grain

Fact на уровне «клик» на миллиардные данные — тяжело. Иногда лучше daily aggregate.

Связанные темы

FAQ

Fact всегда больше dimension?

Обычно да. Fact — события (много), dimension — сущности (меньше).

SCD1 или SCD2?

SCD2 если нужна история. SCD1 если достаточно текущего значения.

Star или snowflake schema?

Star для DWH практически всегда. Snowflake — академически «правильно», но сложнее.

Может быть fact без dimension?

Технически да, но редко полезно. Обычно есть date dim как минимум.


Тренируйте data modelling — откройте тренажёр с 1500+ вопросами для собесов.