Learning Platform
Глоссарий Troubleshooting
Урок 12.02 · 18 мин
Начальный
kimballdesign-processgraindimensional-modeling

Четырёхшаговый процесс дизайна

Прошлый урок показал, как выглядит star schema. Этот урок отвечает на следующий вопрос: как к ней прийти? С чего начать, столкнувшись с реальными бизнес-данными, и в каком порядке принимать решения?

Кимбалл дал на это чёткий ответ — четырёхшаговый процесс дизайна (four-step dimensional design process). Это не бюрократическая формальность, а проверенная последовательность, где каждый шаг опирается на предыдущий, и порядок строго обязателен. Перепутайте шаги — и получите модель, которая отвечает не на те вопросы или вообще не сходится. Этот урок разбирает все четыре шага по очереди на сквозном примере и объясняет, почему именно такой порядок.

Четыре шага

Вот они, в обязательном порядке:

  1. Выбрать бизнес-процесс (select the business process).
  2. Объявить grain (declare the grain).
  3. Выбрать dimensions (identify the dimensions).
  4. Выбрать facts (identify the facts).

Каждый последующий шаг имеет смысл только после предыдущего: dimensions выбирают под объявленный grain, facts — под тот же grain и выбранные dimensions. Порядок — это и есть метод.

Четырёхшаговый процесс: каждый шаг опирается на предыдущий
Шаг 1: Бизнес-процессКакое реальное событие бизнеса моделируем — продажа, отгрузка, регистрация
процесс определяет
Шаг 2: GrainЧто представляет одна строка fact-таблицы — самое важное решение всего дизайна
grain задаёт рамку
Шаг 3: DimensionsПо каким разрезам можно резать — выбираются под объявленный grain
к тому же grain
Шаг 4: FactsКакие числа измеряем — должны соответствовать grain

Шаг 1: выбрать бизнес-процесс

Бизнес-процесс — это операционное событие реального мира, которое порождает данные и которые бизнес хочет анализировать. Не отдел, не отчёт, не таблица — именно событие. Примеры процессов: оформление заказа, отгрузка товара со склада, оплата счёта, регистрация пользователя, показ рекламного объявления, обращение в поддержку.

Почему дизайн начинается именно с процесса? Потому что каждый процесс становится одной fact-таблицей. «Продажи» — одна fact-таблица. «Отгрузки» — другая. Выбрать процесс — значит решить, что станет центром конкретной звезды.

Тонкость, на которой спотыкаются джуниоры: не путать процесс и отдел. «Отдел маркетинга» — не процесс. Внутри маркетинга есть процессы: показ объявления, клик, конверсия лида — каждый со своими данными и своей fact-таблицей. Если начать с отдела, получится мешанина разных событий в одной таблице. Начинайте всегда с конкретного глагола-события.

Возьмём сквозной пример на весь урок: сервис проката велосипедов. Бизнес-процесс, который мы моделируем: завершение поездки (один прокат велосипеда от взятия до возврата).

NOTE

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

Шаг 2: объявить grain

Grain (зерно) — это ответ на вопрос: что представляет одна строка fact-таблицы? Какому атомарному факту соответствует одна запись?

Это самое важное решение во всём дизайне. Следующий урок целиком посвящён grain, поэтому здесь — суть и место в процессе. Grain объявляется одним точным предложением. Для нашего примера: одна строка fct_trips — это одна завершённая поездка на велосипеде.

Почему grain объявляют так рано — вторым шагом, до dimensions и facts? Потому что grain задаёт рамку для обоих следующих шагов. Объявленное зерно говорит, что вообще может быть dimension этой таблицы (только то, что имеет одно значение на одну поездку) и что может быть measure (только то, что измеримо на уровне одной поездки). Без объявленного grain шаги 3 и 4 повисают в воздухе — непонятно, под что выбирать.

Главное правило: все строки одной fact-таблицы обязаны быть одного grain. Нельзя в fct_trips держать вперемешку строки «одна поездка» и строки «итог за день». Смешанный grain ломает любую агрегацию: SUM начинает складывать несопоставимое. Один grain на таблицу — железно.

И второе правило, которое разовьёт следующий урок: предпочитайте самый низкий, атомарный grain — самое мелкое событие, какое есть. Одна поездка, а не «итог по станции за день». Атомарное зерно даёт максимум гибкости: из мелких строк можно собрать любой агрегат, а из заранее свёрнутого итога мелкие детали уже не достать.

Grain задаёт, что строка fact-таблицы означает
Объявленный grainОдна строка fct_trips = одна завершённая поездка. Точное предложение, одно на таблицу
разрешает в dimensions
Допустимые dimensionsТолько то, что имеет ровно одно значение на одну поездку: велосипед, клиент, станция, время
разрешает в facts
Допустимые factsТолько то, что измеримо на уровне одной поездки: длительность, расстояние, стоимость

Шаг 3: выбрать dimensions

Dimensions выбираются так: для объявленного grain ответьте на вопрос «как можно описать одно событие этого зерна?». Каждый осмысленный ответ — кандидат в dimension.

Для зерна «одна завершённая поездка» спросим: как описать одну поездку?

  • Когда она произошла — dimension времени (dim_date, возможно dim_time).
  • Какой велосипед использовали — dim_bike (модель, тип, год выпуска).
  • Какой клиент ехал — dim_rider (тип подписки, возрастная группа, город).
  • Откуда взяли велосипед — dim_station в роли станции старта.
  • Куда вернули — dim_station в роли станции финиша.
  • Какая погода была — dim_weather (если данные есть; погода влияет на спрос).

Каждый из этих ответов имеет ровно одно значение на одну поездку — а значит, согласуется с grain. Это критерий проверки: dimension допустима, только если у неё одно значение на одну строку fact-таблицы. У одной поездки один велосипед, один клиент, одна станция старта — всё подходит. А вот «список всех станций, мимо которых проехал велосипед» — не dimension этой fact-таблицы: значений много на одну поездку, grain нарушен.

Каждая dimension даёт пользователю набор разрезов: dim_bike позволит резать поездки по модели и типу велосипеда, dim_rider — по типу подписки, dim_station — по району. Чем богаче dimensions описательными атрибутами, тем больше вопросов сможет задать аналитик.

Шаг 4: выбрать facts

Facts (measures) выбираются последними: для объявленного grain ответьте «что числового можно измерить у одного такого события?».

Для зерна «одна завершённая поездка»:

  • trip_duration_minutes — длительность поездки в минутах.
  • trip_distance_km — пройденное расстояние.
  • fare_amount — сумма оплаты за поездку.
  • calories_burned — оценка сожжённых калорий (если сервис её считает).

Главный критерий тот же — соответствие grain: measure должна быть измерима именно на уровне одной строки. У одной поездки есть своя длительность, своё расстояние, своя стоимость — всё подходит. А вот «средняя длительность поездки по станции» — не measure уровня поездки: это уже агрегат. Агрегаты не хранят в fact-таблице атомарного grain — их вычисляют запросом из атомарных строк. В fact кладут сырые, аддитивные числа одного события; усреднения и доли считают на лету.

WARNING

Частая ошибка — класть в fact-таблицу уже посчитанные средние и проценты. Если в fct_trips положить столбец avg_duration, его нельзя корректно агрегировать: среднее средних не равно общему среднему. В fact кладите аддитивные слагаемые (длительность, расстояние, сумму), а отношения и средние пусть считает запрос: SUM(trip_duration) / COUNT(*). Подробнее про аддитивность measures — в следующем модуле.

Собираем результат

Четыре шага пройдены — складываем star schema проката велосипедов.

-- Шаг 1: процесс = завершение поездки
-- Шаг 2: grain = одна строка = одна завершённая поездка
CREATE TABLE fct_trips (
    -- Шаг 3: dimensions (foreign keys)
    date_key          INT NOT NULL REFERENCES dim_date(date_key),
    bike_key          INT NOT NULL REFERENCES dim_bike(bike_key),
    rider_key         INT NOT NULL REFERENCES dim_rider(rider_key),
    start_station_key INT NOT NULL REFERENCES dim_station(station_key),
    end_station_key   INT NOT NULL REFERENCES dim_station(station_key),
    -- Шаг 4: facts (measures, измеримы на уровне одной поездки)
    trip_duration_minutes INT           NOT NULL,
    trip_distance_km      NUMERIC(8,2)  NOT NULL,
    fare_amount           NUMERIC(8,2)  NOT NULL
);

И запрос — «средняя длительность и выручка по типу велосипеда и месяцу»:

SELECT
    b.bike_type,
    d.month_name,
    COUNT(*)                      AS trips,
    AVG(f.trip_duration_minutes)  AS avg_duration,
    SUM(f.fare_amount)            AS revenue
FROM fct_trips f
JOIN dim_bike b ON f.bike_key = b.bike_key
JOIN dim_date d ON f.date_key = d.date_key
GROUP BY b.bike_type, d.month_name
ORDER BY revenue DESC;

-- bike_type   | month_name | trips  | avg_duration | revenue
-- electric    | July       | 184320 | 23.7         | 920140.00
-- standard    | July       | 311208 | 18.2         | 610870.50
-- ...

Заметьте: среднее avg_duration считается запросом из атомарных строк, а не хранится. Каждая dimension даёт свой разрез. Запрос читается прозрачно — таков результат прохождения четырёх шагов по порядку.

ШагВопросРезультат для примера
1. Процесскакое событие моделируем?завершение поездки
2. Grainчто означает одна строка?одна завершённая поездка
3. Dimensionsкак описать одно событие?дата, велосипед, клиент, станции
4. Factsчто числового измерить?длительность, расстояние, оплата

Почему порядок нельзя менять

Стоит явно объяснить, почему именно такая последовательность и почему перестановка ломает дизайн.

Процесс перед grain. Пока не выбран процесс, нечему объявлять зерно — grain это всегда зерно конкретного процесса. «Одна строка чего?» — сначала ответьте «чего».

Grain перед dimensions и facts. Это ключевое. Grain определяет, что вообще допустимо в шагах 3 и 4. Если выбирать dimensions до объявления grain, легко взять разрез, который не имеет одного значения на строку, — и модель не сойдётся. Объявленный grain — это фильтр-критерий: dimension годится, если одно значение на строку; measure годится, если измерима на строке. Без grain нет критерия, выбор превращается в гадание.

Facts последними. Facts проверяются на соответствие grain и часто привязаны к набору dimensions. Их выбирают, когда рамка (grain) и контекст (dimensions) уже зафиксированы.

Этот порядок — не ритуал, а защита от самой частой ошибки размерного моделирования: смешанного или плохо определённого grain. Дисциплина «процесс -> grain -> dimensions -> facts» гарантирует, что зерно объявлено явно и рано, а всё остальное согласовано с ним.

Четырёхшаговый процесс на практике — Kimball-light в dbt-проекте Размерное моделирование с точки зрения DE-пайплайна
TIP

На собеседовании по data modeling четырёхшаговый процесс — почти гарантированный вопрос. Ответ-формула: «Выбрать бизнес-процесс, объявить grain, выбрать dimensions, выбрать facts — именно в этом порядке, потому что grain задаёт рамку для dimensions и facts, и его нужно зафиксировать раньше них».

Попробуй сам

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

Шаг 1: убедитесь, что это процесс, а не отдел — сформулируйте его глаголом-событием. Шаг 2: объявите grain одним точным предложением и проверьте, что он атомарный. Шаг 3: выпишите 4-6 dimensions, и для каждой проверьте критерий «одно значение на один тикет». Шаг 4: выпишите 3-5 facts, и для каждой проверьте «измеримо на уровне одного тикета»; следите, чтобы не положить туда среднее или процент.

Затем соберите DDL для fct_support_tickets. И ответьте на главный вопрос: что бы пошло не так, если бы вы выбрали dimensions до того, как объявили grain? Приведите конкретный пример разреза, который кажется уместным, но нарушает зерно «один тикет».


Проверка знанийKnowledge check
Назови четыре шага процесса размерного дизайна по Кимбаллу и объясни, почему grain объявляется именно вторым — до выбора dimensions и facts.
ОтветAnswer
Четыре шага в строгом порядке: 1) выбрать бизнес-процесс — операционное событие реального мира (продажа, отгрузка, поездка), каждый процесс становится одной fact-таблицей; 2) объявить grain — что представляет одна строка fact-таблицы, точным предложением; 3) выбрать dimensions — по каким разрезам можно описать одно событие; 4) выбрать facts — какие числа измеримы на уровне одного события. Grain объявляется вторым, до dimensions и facts, потому что он задаёт рамку и критерий для обоих следующих шагов. Объявленное зерно определяет, что вообще допустимо как dimension (только то, что имеет ровно одно значение на одну строку fact-таблицы) и что допустимо как measure (только то, что измеримо именно на уровне одной строки). Без зафиксированного grain шаги 3 и 4 повисают в воздухе — нет критерия, под что выбирать, и легко взять разрез или показатель, который не согласуется с зерном, из-за чего модель не сойдётся. Процесс идёт перед grain, потому что grain — это всегда зерно конкретного процесса. Facts идут последними, потому что их проверяют на соответствие grain и привязывают к уже выбранным dimensions. Этот порядок защищает от самой частой ошибки размерного моделирования — смешанного или плохо определённого grain.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Каков правильный порядок четырёхшагового процесса размерного дизайна по Кимбаллу?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5