Learning Platform
Глоссарий Troubleshooting
Урок 13.03 · 18 мин
Начальный
accumulating-snapshotfact-tablekimballpipeline

Accumulating snapshot: одна строка на процесс

Два прошлых урока разобрали transaction fact table (строка на событие) и periodic snapshot (строка на состояние за период). Остался третий и последний тип — accumulating snapshot fact table (таблица фактов накапливающихся снимков). Он самый необычный из трёх, потому что нарушает свойство, которое казалось незыблемым: его строки обновляются.

Accumulating snapshot создан для одного конкретного класса задач — анализа процессов с чётко определёнными вехами. Заказ проходит путь: создан -> оплачен -> собран -> отгружен -> доставлен. Этот урок объясняет, что это за тип, почему его строка обновляется, какие у него особые measures и чем он принципиально отличается от первых двух типов.

Зерно: один экземпляр процесса

Зерно accumulating snapshot формулируется так: одна строка — это один экземпляр процесса (workflow), проходящего через предсказуемую последовательность вех (milestones).

Ключевое слово — процесс с вехами. Это не одно событие (как в transaction) и не состояние на дату (как в periodic snapshot). Это сущность, которая проживает многошаговый путь, и шаги известны заранее. Примеры таких процессов:

  • Заказ: создан -> оплачен -> собран на складе -> отгружен -> доставлен.
  • Заявка на кредит: подана -> проверена -> одобрена -> выдана.
  • Производство: запущено -> детали получены -> собрано -> проверено -> отправлено.
  • Обработка страхового случая: заявлен -> назначен эксперт -> оценён -> выплачен.

У всех общая черта: фиксированный набор вех, через которые экземпляр проходит по порядку. Accumulating snapshot моделирует именно такой пайплайн.

Строка обновляется по мере прохождения вех

Вот что делает этот тип уникальным. В transaction fact table строка пишется один раз и неизменна. В periodic snapshot каждый период добавляется новая строка. В accumulating snapshot одна строка живёт весь срок жизни процесса и обновляется каждый раз, когда экземпляр проходит очередную веху.

Посмотрим на структуру fact-таблицы заказов:

-- Accumulating snapshot: grain = один заказ за весь его жизненный цикл
CREATE TABLE fct_order_pipeline (
    order_number          VARCHAR(20) PRIMARY KEY,  -- один экземпляр процесса
    customer_key          INT NOT NULL REFERENCES dim_customer(customer_key),

    -- по одному date-foreign-key на КАЖДУЮ веху процесса
    created_date_key      INT NOT NULL REFERENCES dim_date(date_key),
    paid_date_key         INT REFERENCES dim_date(date_key),
    picked_date_key       INT REFERENCES dim_date(date_key),
    shipped_date_key      INT REFERENCES dim_date(date_key),
    delivered_date_key    INT REFERENCES dim_date(date_key),

    -- measures: длительности между вехами (lag measures)
    days_create_to_pay     INT,
    days_pay_to_ship       INT,
    days_ship_to_deliver   INT,
    days_total             INT,

    order_amount          NUMERIC(12,2) NOT NULL
);

Обратите внимание на две вещи. Первое: по одному date-foreign-key на каждую вехуcreated_date_key, paid_date_key, picked_date_key, shipped_date_key, delivered_date_key. Второе: все вехи, кроме первой, объявлены nullablecreated_date_key обязателен (заказ не существует без создания), остальные могут быть NULL.

Теперь проследим жизнь одной строки.

Заказ создан. В fact-таблицу вставляется строка. created_date_key заполнен; paid_date_key, shipped_date_key, delivered_date_key — пока NULL; длительности — пока NULL. Строка существует, но процесс только начался.

Заказ оплачен (через 2 дня). Та же строка обновляется: проставляется paid_date_key, вычисляется days_create_to_pay = 2. Новая строка не создаётся — обновляется существующая.

Заказ отгружен (ещё через 3 дня). Та же строка снова обновляется: shipped_date_key, days_pay_to_ship = 3.

Заказ доставлен (ещё через 4 дня). Финальное обновление: delivered_date_key, days_ship_to_deliver = 4, days_total = 9. Теперь строка полностью заполнена — процесс завершён.

Одна строка прошла четыре состояния, обновляясь на каждой вехе. NULL-поля постепенно заполнялись по мере прохождения процесса. Это и есть «накапливающийся» снимок: строка накапливает информацию о процессе, пока тот идёт.

Accumulating snapshot: одна строка обновляется на каждой вехе
СозданСтрока вставлена: created_date заполнен, остальные вехи NULL
UPDATE
ОплаченТа же строка обновлена: проставлен paid_date, посчитан days_create_to_pay
ОтгруженТа же строка снова обновлена: shipped_date, days_pay_to_ship
UPDATE
ДоставленФинальное обновление той же строки: delivered_date, days_total — процесс завершён

Несколько date-foreign-keys и lag measures

У accumulating snapshot две характерные особенности структуры, прямо вытекающие из его задачи.

Много date-foreign-keys — по одному на веху. Transaction fact table обычно имеет один date_key — дату события. Accumulating snapshot имеет date-foreign-key на каждую веху процесса: дату создания, дату оплаты, дату отгрузки, дату доставки. Все они ссылаются на одну и ту же dim_date, но в разных ролях (это пример role-playing dimension — подробнее в модуле про dimension-таблицы). Каждый date-key отвечает на «когда заказ достиг этой вехи».

Lag measures — длительности между вехами. Главная аналитическая ценность accumulating snapshot — измерять, сколько времени процесс проводит между вехами. Поэтому его measures — это days_create_to_pay, days_pay_to_ship, days_ship_to_deliver, days_total. Они называются lag measures (измерения задержки) и вычисляются как разница между датами соседних вех. Именно ради них этот тип и существует: он позволяет одним запросом ответить «какова средняя длительность каждого этапа обработки заказа» и «где в пайплайне узкое место».

-- Анализ длительности этапов пайплайна — то, ради чего нужен этот тип
SELECT
    AVG(days_create_to_pay)   AS avg_days_to_pay,
    AVG(days_pay_to_ship)     AS avg_days_to_ship,
    AVG(days_ship_to_deliver) AS avg_days_to_deliver,
    AVG(days_total)           AS avg_total
FROM fct_order_pipeline
WHERE delivered_date_key IS NOT NULL;  -- только завершённые заказы

-- avg_days_to_pay | avg_days_to_ship | avg_days_to_deliver | avg_total
-- 1.4             | 3.8              | 2.1                 | 7.3

Из этого запроса сразу видно: этап «оплата -> отгрузка» (3,8 дня) — самый долгий, вот где узкое место процесса. Такой анализ — родная задача accumulating snapshot, и он даёт ответ из одной строки на заказ, без сложных соединений событий.

TIP

NULL в accumulating snapshot — это не «данные потеряны», а «процесс ещё не дошёл до этой вехи». NULL в shipped_date_key означает «заказ пока не отгружен». Это позволяет одним запросом находить «зависшие» экземпляры: WHERE paid_date_key IS NOT NULL AND shipped_date_key IS NULL — заказы, которые оплачены, но застряли перед отгрузкой. NULL здесь несёт смысл состояния процесса.

Полезно осознать ещё одно следствие. В accumulating snapshot текущее состояние процесса не хранится отдельным столбцом — оно читается из картины заполненных и пустых вех. Заполнены created и paid, пусты shipped и delivered — значит заказ сейчас на этапе «оплачен, ждёт сборки и отгрузки». Не нужен отдельный столбец status с текстом «оплачен»: статус выводится из того, до какой вехи дошли даты. Это удобно — статус не может рассинхронизироваться с датами вех, потому что он и есть производная от них. Запрос «сколько заказов сейчас застряло на каждом этапе» — это группировка по тому, какая веха последняя заполненная.

Три типа рядом

Соберём все три типа fact-таблиц вместе — это итоговая карта модуля.

СвойствоTransactionPeriodic snapshotAccumulating snapshot
Зерноодно событиесостояние сущности за периододин экземпляр процесса
Строк на сущностьпо одной на каждое событиепо одной на каждый периододна на весь жизненный цикл
Строка обновляетсянет, append-onlyнет, период добавляет строкуда, на каждой вехе
Date-foreign-keysобычно одинодин (период)много (по одному на веху)
Характерные measuresсуммы, количества событияуровни состояния + потокиlag measures (длительности)
Отвечает на вопроскакие события происходиликаким было состояние на моментсколько длятся этапы процесса
Примерпродажи, клики, платежиостатки, балансыпайплайн заказа, заявки

Три типа — это не конкуренты, из которых выбирают один навсегда. Это три инструмента под три разных вопроса, и в одной аналитической системе живут все три одновременно. Заказы магазина: fct_sales (transaction) — что и когда продано; fct_inventory_daily (periodic snapshot) — остатки на складе; fct_order_pipeline (accumulating snapshot) — как быстро обрабатываются заказы. Выбор типа — это всегда ответ на вопрос «что именно мы хотим анализировать»: события, состояние или скорость процесса.

WARNING

Accumulating snapshot — единственный тип fact-таблицы с обновлением строк, и это надо учитывать. Колоночные warehouse (модуль про физику) не любят точечные UPDATE. Поэтому accumulating snapshot применяют для процессов с УМЕРЕННЫМ числом экземпляров и предсказуемым коротким набором вех. Для процессов с миллиардами экземпляров обновление каждой строки было бы слишком дорого — там моделируют пайплайн транзакционными событиями вех.

Accumulating snapshot в контексте DE-пайплайна

Где accumulating snapshot — правильный выбор

Accumulating snapshot подходит, когда выполнены три условия одновременно:

  • Есть процесс с фиксированными вехами — заранее известная последовательность шагов.
  • Экземпляр процесса проходит вехи по порядку, и интересна длительность между ними.
  • Число экземпляров умеренное — не миллиарды.

Типичные случаи: обработка заказа от создания до доставки; рассмотрение заявки на кредит/визу/страховку; производственный цикл изделия; путь кандидата по этапам найма; обработка обращения в поддержку с SLA-вехами. Везде, где бизнес спрашивает «сколько времени занимает каждый этап» и «где процесс застревает», — это accumulating snapshot.

А когда не он. Поток дискретных событий без понятия «процесс с вехами» — transaction fact table. Регулярные снимки уровня (остаток, баланс) — periodic snapshot. Accumulating snapshot — узкоспециализированный тип именно под анализ многошаговых процессов; вне этой задачи его не применяют.

Попробуй сам

Возьмите процесс найма сотрудника. Вехи: заявка получена -> резюме просмотрено -> телефонное интервью -> очное интервью -> оффер сделан -> оффер принят.

Спроектируйте accumulating snapshot fct_hiring_pipeline. Сформулируйте зерно одним предложением. Заведите date-foreign-key на каждую веху — и решите, какие из них NOT NULL, а какие nullable, и почему. Заведите lag measures — длительности между соседними вехами и общую длительность.

Затем проследите жизнь одной строки: что записывается при вставке (кандидат подал заявку), и какие поля обновляются на каждой следующей вехе. Напишите два запроса: первый — средняя длительность каждого этапа найма (где узкое место процесса); второй — найти «зависших» кандидатов, у которых было очное интервью, но оффер так и не сделан. Наконец, объясните, почему для этого процесса подходит именно accumulating snapshot, а не transaction fact table — что именно стало бы неудобно при моделировании найма потоком отдельных событий.


Проверка знанийKnowledge check
Что такое accumulating snapshot fact table, почему его строка обновляется и какие у него характерные структурные особенности?
ОтветAnswer
Accumulating snapshot fact table (таблица фактов накапливающихся снимков) — третий тип fact-таблицы по Кимбаллу, созданный для анализа процессов с чётко определёнными вехами. Его зерно: одна строка — это один экземпляр процесса (workflow), проходящего через предсказуемую последовательность вех (заказ: создан -> оплачен -> собран -> отгружен -> доставлен). Это уникальный тип, потому что его строка обновляется — в отличие от transaction fact table (строка пишется раз и неизменна, append-only) и periodic snapshot (каждый период добавляет новую строку). Одна строка accumulating snapshot живёт весь срок жизни процесса: при старте процесса она вставляется с заполненной первой вехой и NULL в остальных, а затем обновляется каждый раз, когда экземпляр проходит очередную веху — проставляется дата вехи и пересчитываются длительности. NULL-поля постепенно заполняются по мере прохождения, строка накапливает информацию о процессе. Две характерные структурные особенности. Первая: много date-foreign-keys — по одному на каждую веху (дата создания, оплаты, отгрузки, доставки), все ссылаются на одну dim_date в разных ролях; первая веха NOT NULL, остальные nullable. Вторая: lag measures — длительности между соседними вехами (days_create_to_pay, days_pay_to_ship, days_total), вычисляемые как разница дат; ради них этот тип и существует — он позволяет одним запросом измерить среднюю длительность каждого этапа и найти узкое место процесса. NULL здесь несёт смысл "процесс ещё не дошёл до этой вехи", что позволяет находить зависшие экземпляры. Accumulating snapshot применяют для процессов с умеренным числом экземпляров и фиксированным коротким набором вех, потому что обновление строк дорого для колоночных warehouse.

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

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Что уникально для accumulating snapshot fact table по сравнению с двумя другими типами?

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

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

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

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