Схема звезды: факты и измерения
Урок объясняет звёздную схему — основной способ моделировать данные для аналитики.
Схема звезды (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-ов.
- Факты отвечают на «сколько», измерения — «по какому разрезу».