首页/数据/数仓建模速查/拉链维度:起止日期设计
维度与事实设计149 条

拉链维度:起止日期设计

用 start_date / end_date 描述每个版本的生效区间;建议闭开区间 [start, end),当前行 end 取远期极大值而非 NULL。

数仓建模速查数据更新于 2026-10-13

拉链维度的区间设计

拉链维度用 start_date 与 end_date 描述每个版本的生效区间。推荐使用闭开区间 [start, end):起始日包含、结束日不包含,这样相邻版本不会在边界日期重叠,同一天只会命中一条记录。当前有效行的 end_date 建议取远期极大值(如 9999-12-31)而不是 NULL,原因是 NULL 参与比较时索引与分区裁剪往往失效,查询条件也变得难以统一书写。

基本信息

区间约定闭开区间 [start_date, end_date)
当前行表示end_date = 9999-12-31,避免使用 NULL
约束同一自然键的版本区间不重叠、不断档
查询写法业务时间 >= start_date AND 业务时间 < end_date

典型使用场景

  • 历史归属还原:订单按下单时间关联当时生效的用户等级版本
  • 当前值查询:end_date 取远期值后可用同一套条件覆盖当前与历史
  • 区间完整性校验:用窗口函数检查同键版本是否存在重叠或断档
  • 分区裁剪:非 NULL 的区间端点让过滤条件能走分区与索引
  • 增量校验:每次加载后验证单键单日仅命中一行

区间查询与校验

区间查询与校验
-- 按业务时间取当时版本(闭开区间)
SELECT * FROM dim_user d
WHERE :dt >= d.start_date
  AND :dt <  d.end_date;      -- 结束日不包含,避免边界重复命中

-- 校验一:同一自然键版本区间是否重叠
SELECT user_id, start_date, end_date,
       lead(start_date) OVER (PARTITION BY user_id ORDER BY start_date) AS next_start
FROM dim_user;
-- 若 next_start < end_date 则存在区间重叠

-- 校验二:当前版本是否唯一
-- SELECT user_id, count(1) FROM dim_user WHERE is_current = 1
-- GROUP BY user_id HAVING count(1) > 1;

区间方案对照

方案写法影响
闭开区间[start, end)相邻版本不重叠,推荐
全闭区间[start, end]边界日期命中两条,指标翻倍
当前行用 NULLend_date IS NULL索引与分区裁剪失效,查询变慢
当前行取远期值9999-12-31条件统一,性能与可读性都好
区间约定必须在建模规范里写死并全局统一。混用闭开与全闭的团队,问题往往表现为偶发的金额翻倍,排查时很难联想到区间边界。
版本区间还应覆盖完全、不留空洞:某成员在两个版本之间出现日期空档时,按时间关联会整段丢失该成员的数据,且不会报错。

易错点

  • 用 NULL 表示当前行:区间比较无法走索引,大维表上的关联查询明显变慢
  • 使用全闭区间:边界日期同时命中两条版本,金额被重复累计
  • 区间存在断档:空档期的事实关联不到维度,数据静默丢失
  • 切换日期字段时未回刷历史区间:新旧口径混在一张表里,版本连续性无从校验

数据来源与参考

  • 数据仓库工具箱
  • 拉链表实现实践
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌