Kimball: star schema — fact в центре, dimensions вокруг
Прошлый модуль закончился диагнозом: нормализованная схема плоха для аналитики. Десятки JOIN на простой вопрос, тихие ошибки в условиях соединения, хрупкость к изменениям. Этот модуль даёт лекарство — размерное моделирование (dimensional modeling), подход, который Ральф Кимбалл сформулировал в книге «The Data Warehouse Toolkit» (1996) и который остаётся основным способом моделировать аналитические данные по сей день.
Центральная фигура размерного моделирования — star schema (звёздная схема). Этот урок объясняет, что это за фигура, из каких двух типов таблиц она состоит и почему именно такая форма делает аналитические запросы быстрыми и простыми. Star schema — это базовый словарь всего, что вы будете изучать дальше; без неё остальные уроки модуля не имеют опоры.
Две роли данных: измерять и описывать
Размерное моделирование начинается с одного наблюдения о природе аналитических данных. Любой бизнес-вопрос разделяется на две части.
Возьмём вопрос: «Сколько денег мы заработали на электронике в Германии в марте?» В нём есть:
- То, что измеряем — «сколько денег». Это число. Его суммируют, усредняют, считают. На английском — measure (мера, показатель).
- То, по чему режем — «электроника», «Германия», «март». Это контекст: категория товара, страна, период. По нему фильтруют и группируют. На английском — dimension (измерение, разрез).
Это разделение фундаментально. Числа, которые мы считаем (выручка, количество, стоимость), и контекст, по которому мы их режем (товар, клиент, время, география), — это две разные сущности по своей роли. Размерное моделирование берёт это интуитивное разделение и делает его структурой базы данных: числа складываются в один тип таблиц, контекст — в другой.
- Fact table (таблица фактов) хранит measures — числовые показатели бизнес-событий.
- Dimension table (таблица измерений) хранит описательный контекст — атрибуты, по которым эти показатели режут.
Форма звезды
Теперь соберём из этих двух типов таблиц схему. В центре — одна fact-таблица. Вокруг неё — несколько dimension-таблиц, каждая соединена с центром одним foreign key. Нарисованная на бумаге, такая структура выглядит как звезда: центр и лучи. Отсюда название — star schema.
Возьмём конкретный пример: аналитика продаж интернет-магазина.
-- Центр звезды: fact-таблица продаж
CREATE TABLE fct_sales (
date_key INT NOT NULL REFERENCES dim_date(date_key),
product_key INT NOT NULL REFERENCES dim_product(product_key),
customer_key INT NOT NULL REFERENCES dim_customer(customer_key),
store_key INT NOT NULL REFERENCES dim_store(store_key),
-- measures: числа, которые мы анализируем
quantity INT NOT NULL,
sales_amount NUMERIC(12,2) NOT NULL,
discount_amount NUMERIC(12,2) NOT NULL
);
-- Луч звезды: dimension товара
CREATE TABLE dim_product (
product_key INT PRIMARY KEY, -- surrogate key
product_name VARCHAR(200),
category VARCHAR(80),
department VARCHAR(80),
brand VARCHAR(80),
unit_cost NUMERIC(10,2)
);
-- Луч звезды: dimension географии и магазина
CREATE TABLE dim_store (
store_key INT PRIMARY KEY,
store_name VARCHAR(120),
city VARCHAR(80),
region VARCHAR(80),
country VARCHAR(80)
);
Обратите внимание на структуру fct_sales. В ней только два сорта столбцов: foreign keys на dimensions (date_key, product_key, customer_key, store_key) и measures (quantity, sales_amount, discount_amount). Ничего описательного — никаких названий категорий или городов прямо в fact. Описания все в dimensions.
И обратите внимание на dim_store. Город, регион, страна — всё в одной таблице, плоско, рядом. В нормализованной схеме (прошлый модуль) это была бы цепочка cities -> regions -> countries из трёх таблиц. Здесь — одна. Dimension денормализована намеренно: описательные атрибуты сведены вместе, чтобы до них был один шаг, а не цепочка соединений.
Почему звезда решает проблему каскада JOIN
Вернёмся к больному вопросу прошлого модуля — «выручка по департаментам и странам». На нормализованной схеме это были восемь JOIN через цепочки справочников. На star schema:
SELECT
p.department,
s.country,
SUM(f.sales_amount) AS revenue
FROM fct_sales f
JOIN dim_product p ON f.product_key = p.product_key
JOIN dim_store s ON f.store_key = s.store_key
GROUP BY p.department, s.country
ORDER BY revenue DESC;
-- department | country | revenue
-- Electronics | Germany | 4920140.00
-- Apparel | France | 3110870.50
-- ...
Два JOIN вместо восьми. И это не случайность одного запроса — это структурное свойство star schema. В звезде между fact и любой dimension ровно один шаг: один foreign key. Поэтому число JOIN в любом аналитическом запросе равно числу разрезов, которые в нём участвуют, и не больше. Нужны товар и география — два JOIN. Нужны только товар и время — два JOIN. Нет глубоких цепочек справочников, которые приходится проходить целиком.
Из этой простой формы вытекают сразу несколько выгод:
- Предсказуемость. Геометрия запроса всегда одна: fact в центре, JOIN-лучи к нужным dimensions. Аналитику не нужно каждый раз заново разбираться, как соединить таблицы.
- Меньше ошибок. Два-три JOIN с очевидными условиями
fact_key = dim_keyошибиться трудно. Восемь JOIN через справочные цепочки — легко. - Производительность. Движки warehouse знают форму звезды и оптимизируют её специально (star-join optimization). Плюс fact-таблица узкая и колоночная — она хорошо сжимается и быстро сканируется (прошлый модуль).
- Понятность для не-инженеров. Star schema читается почти как естественный язык: «суммируй sales_amount из fct_sales, разрезав по department из dim_product». Аналитик и бизнес-пользователь видят структуру сразу.
Существует вариация — snowflake schema, где dimension не денормализована, а разбита на цепочку нормализованных таблиц (department вынесен из dim_product в отдельную таблицу). Снежинка экономит немного места, но возвращает часть каскада JOIN. Кимбалл в общем случае рекомендует именно star — плоские денормализованные dimensions. Подробнее snowflaking — в следующем модуле про dimension-таблицы.
Fact длинная и узкая, dimension короткая и широкая
У двух типов таблиц звезды противоположная геометрия, и это не случайно — она прямо следует из их роли.
Fact-таблица — длинная и узкая. Длинная: одна строка на каждое бизнес-событие, а событий очень много — продаж миллионы и миллиарды. Узкая: в ней лишь горстка foreign keys и горстка measures, обычно 10-20 столбцов. Длинная и узкая — идеальный профиль для колоночного хранилища: миллиарды строк сжимаются, а запросы читают немного столбцов.
Dimension-таблица — короткая и широкая. Короткая: строк относительно мало. Товаров — десятки тысяч, магазинов — сотни, стран — пара сотен. Широкая: много описательных атрибутов, иногда 50-100 столбцов, потому что dimension денормализована и собирает в себя всю описательную цепочку. Каждый атрибут dimension — это потенциальный разрез для отчёта, поэтому атрибутов делают щедро.
| Свойство | Fact table | Dimension table |
|---|---|---|
| Что хранит | числовые measures бизнес-событий | описательные атрибуты контекста |
| Строк | очень много (миллионы-миллиарды) | относительно мало |
| Столбцов | мало (FK + measures) | много (атрибуты, денормализовано) |
| Геометрия | длинная и узкая | короткая и широкая |
| Роль в запросе | то, что суммируют | то, по чему режут (GROUP BY, WHERE) |
| Растёт | постоянно, с каждым событием | медленно |
Запомните эту асимметрию — она пронизывает весь остаток курса. Когда вы видите таблицу с миллиардом строк и десятком столбцов, почти наверняка перед вами fact. Когда таблицу с десятками тысяч строк и сотней описательных столбцов — почти наверняка dimension.
Простой тест, fact это или dimension: спросите «эту таблицу складывают или по ней группируют?». Если столбцы таблицы суммируют (SUM, AVG) — это measures, таблица fact. Если столбцы попадают в GROUP BY и WHERE как разрезы и фильтры — это атрибуты, таблица dimension. Числа измеряют, атрибуты описывают.
Star schema — gold-слой, а не источник истины
Важная оговорка, чтобы связать модуль с предыдущим. Star schema — не первое, что происходит с данными. Вспомните medallion architecture: bronze (сырьё), silver (очищенное), gold (бизнес-готовое). Размерная модель — это gold-слой. Star schema строится поверх очищенных данных, как финальная форма для потребления дашбордами и аналитиками.
Поэтому современный консенсус (исследования 2025-2026) формулируется так: размерное моделирование по Кимбаллу — по-прежнему самая распространённая техника для аналитики, dbt прямо рекомендует строить именно Kimball-модели. Но star schema всё чаще не источник истины, а слой потребления поверх нормализованного или иного промежуточного слоя. Звезда — не «вся архитектура данных», а её витринная, обращённая к пользователю часть. Это снимает ложную дилемму «нормализация против размерного моделирования»: они живут на разных слоях и решают разные задачи. Нормализация — для приёма и согласования, звезда — для подачи.
Star schema: обзор для начинающего DE Kimball-light в dbt: staging, intermediate, martsПопробуй сам
Возьмите знакомую предметную область — например, сервис доставки еды. Бизнес задаёт вопросы: «выручка по районам города и дням недели», «средний чек по типам кухни», «число заказов по ресторанам за месяц».
Сначала для каждого вопроса разделите его на measure (что считаем) и dimensions (по чему режем). Затем спроектируйте star schema на бумаге: одна fact-таблица в центре и dimension-таблицы вокруг. Решите, какие measures лягут в fact (выручка, число заказов, сумма чека) и какие foreign keys ей нужны. Для каждой dimension выпишите 5-8 описательных атрибутов — например, для dimension ресторана: название, тип кухни, район, ценовая категория, рейтинг.
Финальная проверка: возьмите любой из трёх вопросов и напишите для своей звезды SQL-запрос. Посчитайте, сколько JOIN в нём получилось, и убедитесь, что их ровно столько, сколько разрезов в вопросе.