Детальнее про pit и bridge таблицы
This commit is contained in:
+116
-13
@@ -286,37 +286,140 @@ Business Vault (BV) — следующий слой над Raw DV:
|
|||||||
|
|
||||||
#### 5.2.1. PIT-таблицы (Point-in-Time)
|
#### 5.2.1. PIT-таблицы (Point-in-Time)
|
||||||
|
|
||||||
PIT (Point-in-Time) отвечает на вопрос:
|
PIT (Point-in-Time) решает очень конкретную боль:
|
||||||
|
|
||||||
> Как объект выглядел на дату X?
|
> «Покажи, как объект выглядел **на дату X**, но так, чтобы запрос был простым».
|
||||||
|
|
||||||
Вместо того чтобы каждый раз писать сложные запросы с диапазонами `valid_from` / `valid_to`, мы заранее готовим таблицу с нужными ссылками на версии сателлитов.
|
Если у нас есть несколько сателлитов с историей (например, `sat_customer_info`, `sat_customer_segment`, `sat_customer_risk`), то без PIT любой запрос превращается в пачку условий
|
||||||
|
`BETWEEN valid_from AND valid_to` или оконных функций.
|
||||||
|
|
||||||
Простейший эскиз:
|
**Идея PIT:**
|
||||||
|
|
||||||
|
Мы заранее считаем «словарь» вида:
|
||||||
|
|
||||||
|
> для каждой пары (объект, дата) — какие версии сателлитов на эту дату актуальны.
|
||||||
|
|
||||||
|
Упрощённый пример структуры:
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
CREATE TABLE pit_customer_daily (
|
CREATE TABLE pit_customer_daily (
|
||||||
hk_customer BYTEA,
|
hk_customer BYTEA,
|
||||||
as_of_date DATE,
|
as_of_date DATE,
|
||||||
hk_sat_info BYTEA, -- ссылка на нужную версию sat_customer_info
|
hk_sat_info BYTEA, -- sat_customer_info на эту дату
|
||||||
hk_sat_segment BYTEA, -- ссылка на нужную версию sat_customer_segment
|
hk_sat_segment BYTEA, -- sat_customer_segment на эту дату
|
||||||
load_dttm TIMESTAMP
|
load_dttm TIMESTAMP
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
Теперь, чтобы собрать витрину продаж, достаточно один раз присоединить PIT по дате.
|
Внутри логически это выглядит так:
|
||||||
|
|
||||||
|
| hk_customer | as_of_date | hk_sat_info | hk_sat_segment |
|
||||||
|
| ----------- | ---------- | ------------ | -------------- |
|
||||||
|
| 101 | 2023-06-10 | hash_info_v1 | hash_seg_v1 |
|
||||||
|
| 101 | 2023-06-11 | hash_info_v2 | hash_seg_v1 |
|
||||||
|
| 101 | 2023-06-12 | hash_info_v2 | hash_seg_v2 |
|
||||||
|
|
||||||
|
Дальше витрина продаж вместо сложных `BETWEEN` делает простой JOIN:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
s.sale_date,
|
||||||
|
s.amount,
|
||||||
|
seg.segment,
|
||||||
|
info.city
|
||||||
|
FROM fact_sales s
|
||||||
|
JOIN pit_customer_daily pit
|
||||||
|
ON pit.hk_customer = s.hk_customer
|
||||||
|
AND pit.as_of_date = s.sale_date
|
||||||
|
JOIN sat_customer_segment seg
|
||||||
|
ON seg.hk_customer = pit.hk_customer
|
||||||
|
AND seg.hashdiff = pit.hk_sat_segment
|
||||||
|
JOIN sat_customer_info info
|
||||||
|
ON info.hk_customer = pit.hk_customer
|
||||||
|
AND info.hashdiff = pit.hk_sat_info;
|
||||||
|
```
|
||||||
|
|
||||||
|
Мы **один раз** дорого посчитали PIT (по расписанию),
|
||||||
|
и дальше все витрины просто используют эту «справочную таблицу».
|
||||||
|
|
||||||
|
Коротко: **PIT — это таблица, где заранее записано, какая версия данных была актуальна на дату X.**
|
||||||
|
|
||||||
#### 5.2.2. Bridge-таблицы
|
#### 5.2.2. Bridge-таблицы
|
||||||
|
|
||||||
Bridge-таблицы помогают проходить по сложным цепочкам связей:
|
Bridge-таблицы отвечают на другой вопрос:
|
||||||
|
|
||||||
* от клиента ко всем его договорам;
|
> «Какие объекты **в итоге** связаны между собой через длинную цепочку Links?»
|
||||||
* от договора ко всем счетам;
|
|
||||||
* от счёта ко всем транзакциям.
|
|
||||||
|
|
||||||
Вместо того чтобы каждый раз писать длинный JOIN по нескольким Links, мы один раз строим Bridge и далее используем его как предрасчитанный путь.
|
В Data Vault связь между двумя сущностями редко бывает «одним JOIN’ом». Чаще это цепочка:
|
||||||
|
|
||||||
#### 5.2.3. Business-правила и derived-таблицы
|
* клиент → договор → счёт → карта → транзакция;
|
||||||
|
* подразделение → филиал → торговая точка → чек;
|
||||||
|
* компания → дочерняя компания → проект → контракт → платёж.
|
||||||
|
|
||||||
|
Если каждый раз в витринах писать все эти JOIN’ы, запросы становятся:
|
||||||
|
|
||||||
|
* длинными и хрупкими;
|
||||||
|
* плохо читаемыми;
|
||||||
|
* дублируются во множестве отчётов.
|
||||||
|
|
||||||
|
**Идея Bridge:**
|
||||||
|
|
||||||
|
Сделать отдельную таблицу, где заранее развернуть «финальные» пары:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE bridge_customer_trx (
|
||||||
|
hk_customer BYTEA,
|
||||||
|
hk_trx BYTEA
|
||||||
|
-- опционально: даты действия связи, тип связи и т.п.
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Такую таблицу мы считаем **в ETL**, один раз по расписанию, а не в каждом запросе.
|
||||||
|
Условный псевдокод построения:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO bridge_customer_trx (hk_customer, hk_trx)
|
||||||
|
SELECT DISTINCT
|
||||||
|
c.hk_customer,
|
||||||
|
t.hk_trx
|
||||||
|
FROM hub_customer c
|
||||||
|
JOIN link_customer_contract lcc ON lcc.hk_customer = c.hk_customer
|
||||||
|
JOIN hub_contract ct ON ct.hk_contract = lcc.hk_contract
|
||||||
|
JOIN link_contract_account lca ON lca.hk_contract = ct.hk_contract
|
||||||
|
JOIN hub_account acc ON acc.hk_account = lca.hk_account
|
||||||
|
JOIN link_account_card lac ON lac.hk_account = acc.hk_account
|
||||||
|
JOIN hub_card card ON card.hk_card = lac.hk_card
|
||||||
|
JOIN link_card_trx lct ON lct.hk_card = card.hk_card
|
||||||
|
JOIN hub_trx t ON t.hk_trx = lct.hk_trx;
|
||||||
|
```
|
||||||
|
|
||||||
|
После этого витрина может использовать уже готовый Bridge:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
SELECT
|
||||||
|
c.customer_bk,
|
||||||
|
SUM(t.amount) AS total_amount
|
||||||
|
FROM hub_customer c
|
||||||
|
JOIN bridge_customer_trx bct
|
||||||
|
ON bct.hk_customer = c.hk_customer
|
||||||
|
JOIN hub_trx t
|
||||||
|
ON t.hk_trx = bct.hk_trx
|
||||||
|
WHERE t.trx_date >= CURRENT_DATE - INTERVAL '30 day'
|
||||||
|
GROUP BY c.customer_bk;
|
||||||
|
```
|
||||||
|
|
||||||
|
Сложный путь по Links мы спрятали внутрь bridge‑таблицы,
|
||||||
|
а витрины работают с простой связкой «клиент ↔ транзакция».
|
||||||
|
|
||||||
|
Коротко:
|
||||||
|
|
||||||
|
* **PIT** — про «какая версия атрибутов была на дату X»;
|
||||||
|
* **Bridge** — про «какие объекты в итоге связаны друг с другом по длинному маршруту».
|
||||||
|
|
||||||
|
Bridge — это **не обязательный элемент DV**, а инструмент оптимизации.
|
||||||
|
Он нужен тогда, когда цепочки Links становятся длинными и повторяются во многих отчётах.
|
||||||
|
|
||||||
|
#### 5.2.3. Business-правила и derived-таблицы Business-правила и derived-таблицы
|
||||||
|
|
||||||
В BV логично размещать бизнес-логику, которая:
|
В BV логично размещать бизнес-логику, которая:
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user