SQL 按有效期关联维表,为什么一笔订单会匹配两条记录?
面试怎么回答
我会先统一事实时间与维度有效期的时区和精度,通常采用左闭右开的区间,避免交界点同时命中两版。随后检查同业务键的版本是否重叠,不能用任意取一条掩盖问题。关联后按事实主键核对零命中、一命中和多命中,再验证金额守恒。零命中要保留未知归属或隔离原因,多命中应修复维度或执行明确的版本优先规则。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
一笔订单金额100元,关联历史门店维度后变成了两行,汇总结果也成了200元。开发解释:“我已经加了生效时间和失效时间。”问题恰恰可能就在这两个时间之间:边界怎么算,版本有没有重叠?
有效期关联要同时约束时间边界和版本唯一性;闭开区间解决交界,重叠治理解决一事实多命中。
先区分交界重复与真正重叠
假设旧版门店归属到9月1日零点结束,新版从同一时刻开始。如果两个条件都包含端点,刚好零点发生的订单就会匹配两版。
常见写法是 BETWEEN,它包含左右边界。处理连续版本时,更清晰的约定是左闭右开:发生时间大于等于生效时间,并且小于失效时间。
但闭开区间不是万能修复。如果旧版写到9月5日,新版9月1日已经开始,两版之间真的重叠了四天。改比较符号不能让这四天消失。
一次正确关联需要三份约定
第一份是时间含义。订单创建、支付和结算时间可能对应不同业务归属,要依据指标选择,不能默认所有事实都用创建时间。
第二份是时间精度。源端以日期表示生效,事实精确到毫秒,应先解释这一天从哪个时区的零点开始。不能为了关联方便,把事实时间截断成日期,导致日内调整失去信息。
第三份是开口版本。当前版本的失效时间可以用明确的未来上界,也可以为 NULL;两种设计都可以,但条件必须与约定一致,不能让 NULL 通过普通比较后全部不命中。
SQL 的核心条件其实不长
以下是通用关联形态,字段类型和时区需要预先统一:
SELECT f.order_id, f.amount, d.region
FROM fact_order f
LEFT JOIN dim_store_history d
ON f.store_id = d.store_id
AND f.paid_at >= d.valid_from
AND (f.paid_at < d.valid_to OR d.valid_to IS NULL);
这段 SQL 的前提是同一门店在任一业务时点最多一版有效。SQL 本身没有替你证明这个前提。

一个典型场景:总量验收为什么漏了?
比如旧版区间是1日至5日,新版是3日起,4日订单命中两条。最初以为订单主表有重复,回查主键却完全唯一。
根因在维度版本重叠。旧验收只验证维度代理键唯一,每个版本的代理键当然不同;它没有验证“同业务键在同一时点只能有一个有效版本”。
修复先找出重叠范围及受影响订单,再由业务确认正确版本。补上版本区间检查,关联后统计每个事实键的命中数,最后核对金额与订单数。
不要直接 ROW_NUMBER 随机保留一条。这样会让金额暂时恢复,却可能把历史区域分错;只有业务允许并明确记录优先级时,选择一版才是可解释的处理。
检查重叠,也要防止只比较上一行
一个很长的区间可能包住后面几个短区间。如果只把当前生效日与上一行失效日比较,碰上嵌套和异常短区间,就可能遗漏更早长区间的覆盖。
可用区间自关联直接找冲突,或者按生效日排序,将当前起点与此前所有区间的最大终点比较。开放终点要按无穷上界处理,不能被聚合忽略。
区间空洞与重叠是两个问题:空洞可能导致未知归属,重叠导致多命中。是否允许空洞由业务决定;不要把补齐日期误认为版本治理已经完成。
上线验收至少包含交界时刻、边界前后一刻、嵌套重叠、开放版本和完全未命中。真正重要的是把关联正确性变成可验证的约束,而不是只让查询能够执行。
资料与适用边界
机制以所链接版本为准;业务示例用于说明判断方法,不代表作者真实项目或已验证的生产性能。