Схема звезды: факты и измерения

Урок объясняет звёздную схему — основной способ моделировать данные для аналитики.

Схема звезды (star schema) — модель данных, где центральная таблица фактов (events, продажи) связана с таблицами измерений (товары, клиенты, даты).

Факты и измерения

В аналитике данные делят на два вида. Факты — это события с числами, которые мы агрегируем: продажа на 500 рублей, клик, просмотр. Измерения (dimensions) — это контекст: какой товар, какой клиент, в какой день. Факты отвечают на «сколько», измерения — на «по чему разрезать».

        dim_products
             |
dim_dates --- fact_sales --- dim_customers
             |
        dim_stores

Таблица фактов в центре, измерения по краям — отсюда и название «звезда». Каждая строка факта ссылается на измерения по их ключам.

Зачем денормализация

В обычной (нормализованной) базе данные дробят на множество мелких таблиц, чтобы не дублировать. Для аналитики это плохо: запрос «выручка по городам за месяц» собирает кучу JOIN-ов и тормозит. Звезда специально денормализована: измерений мало, они широкие, JOIN-ов в запросе минимум.

Почему именно «звезда», а не одна большая плоская таблица? Потому что контекст (название товара, его категория, бренд) повторяется в тысячах продаж, и хранить его внутри каждой строки факта расточительно и неудобно для обновления. Измерения выносят эти повторяющиеся атрибуты в отдельные компактные таблицы, а факты ссылаются на них по ключу. Получается баланс: контекст не дублируется, а запросов с JOIN-ами всё равно мало — обычно один-два.

Как работает под капотом

Соберём мини-звезду: факты продаж и два измерения, затем посчитаем витрину «выручка по категориям».

CREATE TABLE dim_product (id INTEGER PRIMARY KEY, name TEXT, category TEXT);
CREATE TABLE dim_date (id INTEGER PRIMARY KEY, day TEXT, month TEXT);
CREATE TABLE fact_sales (id INTEGER PRIMARY KEY, product_id INTEGER, date_id INTEGER, amount INTEGER);

INSERT INTO dim_product VALUES (1,'Книга','Медиа'), (2,'Наушники','Электроника');
INSERT INTO dim_date VALUES (1,'2026-06-01','июнь'), (2,'2026-06-02','июнь');
INSERT INTO fact_sales VALUES
    (1,1,1,300), (2,2,1,5000), (3,1,2,600), (4,2,2,4500);

SELECT p.category, SUM(f.amount) AS revenue
FROM fact_sales f
JOIN dim_product p ON p.id = f.product_id
GROUP BY p.category
ORDER BY revenue DESC;

Запрос читается почти как бизнес-вопрос: «возьми факты продаж, присоедини измерение товаров, сгруппируй по категории». Аналитику легко его понять и расширить — например, добавить JOIN dim_date и разрезать по месяцам. В этом и сила звезды: модель повторяет ментальную карту бизнеса, поэтому запросы получаются короткими и читаемыми даже у людей, далёких от баз данных.

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

  • Хранить контекст внутри фактов как текст. Если в каждой строке продаж дублировать полное название и категорию товара, таблица раздувается и обновлять её тяжело — для этого и нужны измерения.
  • Чрезмерная нормализация под аналитику. Десятки мелких таблиц с цепочками JOIN губят скорость запросов; звезда сознательно проще.
  • Смешивать факты и измерения. Числа для агрегации и контекст для разрезов — разные сущности; их не стоит складывать в одну таблицу.

Итог

  • Звезда состоит из таблицы фактов (числа-события) и таблиц измерений (контекст).
  • Она денормализована, чтобы аналитические запросы делали меньше JOIN-ов.
  • Факты отвечают на «сколько», измерения — «по какому разрезу».
Проверьте себя
1. Что хранится в таблице фактов звёздной схемы?
AСправочники товаров и клиентов
BЧисловые события для агрегации (суммы продаж, клики) со ссылками на измерения
CТолько текстовые описания
DМетаданные о конвейере
2. Почему звёздная схема намеренно денормализована?
AЧтобы экономить место на диске
BЧтобы аналитические запросы делали меньше JOIN-ов и работали быстрее
CЧтобы запретить агрегацию
DДенормализация в звезде не используется