docs(warehouse): упрощены объяснения витрин, сессий и пересчета

Зачем: читателю ступеней 1–4 нужно проследить выбор дней, расчет строки
и публикацию результата без расшифровки метафор.

Что: согласованы справочники DM, сессий, ETL и два раздела хранилища
с комментариями шести SQL-примеров. Уточнены ограничения повторного
запуска и выбора атрибутов при равном времени событий.

Проверка: make lint, git diff --check, локальные Markdown-ссылки и якоря.
SQL без комментариев совпадает; AST Python без строк документации совпадает.
Исторические разделы и материалы вне границ задачи сохранены.

Refs: #249
This commit is contained in:
ddadmin committed 2026-09-13 14:33:39 -04:00
1 parent 84dc718f62
commit 5f317ba8d2
11 files changed
+440 -490

No files matched your search

+10 -8
View File
@@ -1,7 +1,8 @@
-- DM: сборка выручки в донор перед заменой дневных партиций.
-- DROP PARTITION ALL очищает локальные таблицы на каждом узле; причина не
-- применять TRUNCATE разобрана в соседнем sql/dds/order_rebuild.sql.
-- DM: сборка выручки за выбранный диапазон в донор.
-- DROP PARTITION ALL очищает донор на каждом узле. Вставка ниже дает
-- одну строку на день заказа и категорию товара: только оплаченные заказы.
-- ARRAY JOIN разворачивает согласованные позиции трех массивов вместе.
-- Причина выбора DROP вместо TRUNCATE — в sql/dds/order_rebuild.sql.
ALTER TABLE dm.revenue_daily_stage_rep ON CLUSTER clickstream_cluster
DROP PARTITION ALL;
@@ -22,8 +23,8 @@ SELECT
orders,
units,
CAST(revenue, 'Decimal(18, 2)'),
-- Обычное деление Decimal усекает доли копейки вместо округления;
-- запас масштаба сохраняет их до денежного результата.
-- Делим с шестью знаками после запятой, затем округляем до копеек.
-- Так дробная часть сохраняется до округления среднего чека.
CAST(
round(divideDecimal(revenue, toDecimal128(orders, 0), 6), 2),
'Decimal(18, 2)'
@@ -49,7 +50,8 @@ FROM
report_date,
product_category
)
-- Источник лежит по заказу, цель — по категории. Ноль заставляет инициатор
-- слить частичные агрегаты и разложить готовые строки по ключу цели.
-- Источник распределен по заказу, цель — по категории. Значение 0 оставляет
-- итоговую агрегацию и распределение по ключу цели на ноде, принявшей запрос.
-- distributed_foreground_insert = 1 ждет доставки строк перед заменой.
SETTINGS distributed_foreground_insert = 1,
parallel_distributed_insert_select = 0;
+6 -6
View File
@@ -1,8 +1,8 @@
-- DM: подмена одного дня выручки готовой партицией донора.
--
-- День приходит из результата revenue_daily_scope.sql, а не от пользователя.
-- Jinja нужна потому, что ClickHouse не подставляет параметр в PARTITION.
-- Замена идет на локальной таблице ON CLUSTER: граница атомарности и повтор
-- разобраны на таком же шаге в sql/dds/order_replace.sql.
-- DM: замена дня выручки готовой партицией донора.
-- Дату из revenue_daily_scope.sql подставляет Jinja: параметр ClickHouse
-- в ALTER ... PARTITION здесь не поддерживается.
-- ON CLUSTER выполняет замену на локальных таблицах каждого шарда.
-- Замена атомарна на одном шарде, общей транзакции для кластера нет.
-- Повтор с тем же донором не добавляет копий строк.
ALTER TABLE dm.revenue_daily_rep ON CLUSTER clickstream_cluster
REPLACE PARTITION '{{ params.day }}' FROM dm.revenue_daily_stage_rep;
+10 -14
View File
@@ -1,14 +1,11 @@
-- DM: объем пересборки выручки.
-- DM: диапазон пересборки выручки, first_day и last_day включительно.
-- Последний слепок ODS задает даты оформления еще изменяемых заказов.
-- Агрегат читает DDS; ODS нужен только для границ диапазона.
-- Левая граница расширяется до первого дня с оплаченными заказами DDS,
-- которого нет в витрине. Так первый запуск охватывает историю выручки.
-- По наличию даты нельзя обнаружить частично заполненный день.
--
-- Последний слепок ODS называет дни, состояние которых источник еще может
-- изменить. Сам агрегат читает только DDS; ODS здесь нужен лишь для границ.
-- Первый запуск и повтор после частичного отказа расширяют левую границу до
-- первого дня оплаченных заказов, которого еще нет в витрине.
-- Почему хвост начинается с первого пропуска и чем за это платит ODS,
-- разобрано в sql/dds/order_scope.sql.
--
-- Файл кончается без точки с запятой: даг читает его одним запросом и ждет
-- одну строку с границами.
-- Точки с запятой в конце нет: драйвер добавляет FORMAT к этому запросу.
WITH
(SELECT max(snapshot_date) FROM ods.order_dist) AS last_snapshot,
(
@@ -24,8 +21,7 @@ WITH
(
SELECT minOrNull(report_date)
FROM (SELECT DISTINCT order_date AS report_date FROM dds.order_v WHERE status = 'paid')
-- Правая сторона тоже распределенная: GLOBAL собирает множество дней
-- на инициаторе и не превращает запрос в запрещенное двойное чтение.
-- GLOBAL собирает список дней витрины со всех шардов.
WHERE report_date GLOBAL NOT IN (
SELECT report_date FROM dm.revenue_daily_dist
)
@@ -33,7 +29,7 @@ WITH
SELECT
least(window_start, coalesce(first_missing_day, window_start)) AS first_day,
window_end AS last_day
-- Без ODS неизвестно окно. Без готового DDS донор останется пустым, а замена
-- сотрет уже собранные дни выручки. Поэтому для прогона нужны оба источника.
-- Для выбора диапазона нужны заказы в ODS, для расчета — в DDS.
-- Эта проверка наличия строк не подтверждает полноту источников.
WHERE (SELECT count() FROM dds.order_v) > 0
AND (SELECT count() FROM ods.order_dist) > 0