# SCD2 缓慢变化维处理规则 本文档定义 `billiards_dwd` 模式下维度表的 SCD2(Slowly Changing Dimension Type 2)处理策略、 生效区间管理和版本控制规则。 > **状态**:骨架文档,各维度表的跟踪字段与变更触发条件待补充。 --- ## 1. 概述 ### 1.1 什么是 SCD2 SCD2 通过保留维度记录的历史版本来追踪属性变化。当被跟踪字段发生变更时: 1. 关闭当前版本(设置结束时间、标记为非当前) 2. 插入新版本(设置开始时间、标记为当前) ### 1.2 实现模块 - 处理器:`scd/scd2_handler.py` — `SCD2Handler` 类 - 核心方法:`upsert(table_name, natural_key, tracked_fields, record, effective_date)` - 返回值:`INSERT`(新记录)、`UPDATE`(属性变更)、`UNCHANGED`(无变化) --- ## 2. SCD2 元数据字段 所有维度表统一包含以下 SCD2 控制字段: | 字段 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `scd2_start_time` | `TIMESTAMPTZ` | `now()` | 版本生效起始时间 | | `scd2_end_time` | `TIMESTAMPTZ` | `'9999-12-31'` | 版本失效时间(`9999-12-31` 表示当前有效) | | `scd2_is_current` | `INT` | `1` | 当前版本标记(`1` = 当前,`0` = 历史) | | `scd2_version` | `INT` | `1` | 版本号(自增) | ### 约束 - 主键:`(natural_key, scd2_start_time)` — 同一自然键的不同版本通过生效时间区分 - 唯一索引:`WHERE scd2_is_current = 1` — 保证每个自然键只有一条当前记录 - 排他约束(GiST):`tstzrange(scd2_start_time, scd2_end_time)` — 防止同一自然键的版本时间段重叠 --- ## 3. 处理流程 ``` 收到维度记录 │ ▼ 按 natural_key 查找 valid_to IS NULL 的当前记录 │ ├── 不存在 → INSERT 新记录(is_current=1, valid_from=now) │ └── 存在 → 比较 tracked_fields │ ├── 无变化 → UNCHANGED(跳过) │ └── 有变化 → UPDATE 旧记录(valid_to=now, is_current=0) INSERT 新记录(valid_from=now, is_current=1) ``` --- ## 4. 维度表 SCD2 配置 ### 4.1 门店维度(dim_site / dim_site_ex) - Schema:`billiards_dwd` - 自然键:`site_id` - 跟踪字段:*(待定义)* - 变更触发场景:*(待补充)* ### 4.2 台桌维度(dim_table / dim_table_ex) - Schema:`billiards_dwd` - 自然键:`table_id` - 跟踪字段:*(待定义)* - 变更触发场景:*(待补充)* ### 4.3 助教维度(dim_assistant / dim_assistant_ex) - Schema:`billiards_dwd` - 自然键:`assistant_id` - 跟踪字段:*(待定义)* - 变更触发场景:*(待补充)* ### 4.4 会员维度(dim_member / dim_member_ex) - Schema:`billiards_dwd` - 自然键:`member_id` - 跟踪字段:*(待定义)* - 变更触发场景:*(待补充)* ### 4.5 会员卡账户维度(dim_member_card_account / dim_member_card_account_ex) - Schema:`billiards_dwd` - 自然键:`member_card_id` - 跟踪字段:*(待定义)* - 变更触发场景:*(待补充)* ### 4.6 商品维度(dim_tenant_goods / dim_tenant_goods_ex / dim_store_goods / dim_store_goods_ex) - Schema:`billiards_dwd` - 自然键:`tenant_goods_id` / `site_goods_id` - 跟踪字段:*(待定义)* - 变更触发场景:*(待补充)* ### 4.7 商品分类维度(dim_goods_category) - Schema:`billiards_dwd` - 自然键:`category_id` - 跟踪字段:*(待定义)* - 变更触发场景:*(待补充)* ### 4.8 团购套餐维度(dim_groupbuy_package / dim_groupbuy_package_ex) - Schema:`billiards_dwd` - 自然键:`groupbuy_package_id` - 跟踪字段:*(待定义)* - 变更触发场景:*(待补充)* --- ## 5. 查询约定 ### 获取当前有效记录 ```sql SELECT * FROM billiards_dwd.dim_member WHERE scd2_is_current = 1; ``` ### 获取某时间点的历史快照 ```sql SELECT * FROM billiards_dwd.dim_member WHERE scd2_start_time <= '2025-06-01' AND scd2_end_time > '2025-06-01'; ``` ### 获取某记录的完整变更历史 ```sql SELECT * FROM billiards_dwd.dim_member WHERE member_id = 12345 ORDER BY scd2_start_time; ``` --- ## 6. 注意事项 - **时区**:`scd2_start_time` / `scd2_end_time` 使用 `TIMESTAMPTZ`,统一以服务器时区存储 - **并发安全**:当前实现在单次 ETL 运行内串行处理,未做行级锁;并发写入需额外保护 - **删除策略**:维度记录不做物理删除,仅通过关闭版本(`scd2_is_current = 0`)标记失效 --- ## 维护约定 - 新增维度表时,须在本文档添加对应章节 - 跟踪字段变更时,须同步更新文档并评估历史数据影响 - 文档统一 UTF-8 编码,中文撰写