SCD 类型1:覆盖——最简单,也最容易被误用
SCD 类型1,说白了就是「直接覆盖」。新数据来了,旧数据直接丢掉,不留任何历史痕迹。这是所有缓慢变化维策略里最粗暴、也最省事的一种。
我个人习惯把它叫做「失忆模式」。为什么?因为一旦覆盖,过去的状态就再也查不回来了。你想想看,这在很多业务场景下其实挺危险的。但话说回来,有些场景它确实是最优解。
核心定义:当维度属性发生变化时,直接用新值覆盖旧值,不保留任何历史版本。
实现方式:就一条 UPDATE 语句的事
实现起来真的没什么技术含量。一条 UPDATE,搞定。我见过不少刚入行的同学,觉得这太简单了,反而会想复杂。其实没必要。
-- 假设我们有一个客户维度表 dim_customer
-- 客户ID = 1001 的邮箱地址需要修正
UPDATE dim_customer
SET email = 'zhang.san@newcompany.com',
update_time = CURRENT_TIMESTAMP
WHERE customer_id = 1001;
嗯,就这么简单。但要注意一点——一定要记录更新时间戳。虽然你丢了历史值,但至少知道「什么时候改的」。我在项目中吃过这个亏,后面会讲到。
适用场景:什么时候该用「失忆模式」?
类型1不是万能的,但有几个场景它特别合适。
| 场景 | 说明 | 我的经验 |
|---|---|---|
| 错误修正 | 数据录入错误,比如电话号码少了一位、邮箱拼写错了 | 这是最常见的场景,我几乎每周都会遇到 |
| 非关键属性 | 对分析没有历史意义的字段,比如客户昵称、临时备注 | 别在这种字段上浪费 SCD 类型2 的存储 |
| 合规擦除 | GDPR 等法规要求删除或更新个人敏感信息 | 这时候你巴不得没有历史记录 |
| 源系统无历史 | 源系统本身就不保留变更历史,你也没办法 | 巧妇难为无米之炊,类型1是唯一选择 |
我的建议:如果你不确定某个字段该不该用类型1,先问自己一个问题——「三个月后,有人需要查这个字段之前的值吗?」如果答案是「不需要」,那就放心用覆盖。
优缺点分析:别只看它简单
类型1的优点很明显,但坑也不少。我一个个说。
优点
- 实现成本极低——一条 SQL,不需要额外的表、不需要拉链表、不需要有效期字段
- 查询性能最好——没有 JOIN 历史表的开销,也不需要过滤有效记录
- 存储最省——只保留一份数据,不会像类型2那样膨胀
- ETL 逻辑简单——MERGE 语句里的 UPDATE 分支,几行代码搞定
缺点
- 历史信息彻底丢失——这是最大的问题。一旦覆盖,再也回不去
- 破坏审计链——如果你需要做数据审计,类型1会让你欲哭无泪
- 报表结果会变——昨天的报表和今天的报表,同一个维度可能算出不同的结果
- 无法回溯分析——比如你想分析「客户在修改邮箱前的行为」,做不到
我曾经踩过的坑:有一次做客户生命周期分析,用了类型1覆盖了客户的「会员等级」字段。结果跑出来的分析结果完全对不上——因为半年前的老客户,等级全被最新的值覆盖了。那次之后,凡是跟「时间」相关的维度属性,我都不敢轻易用类型1。
SQL 示例:完整的 MERGE 实现
实际生产中,我们不会只写一条 UPDATE。通常会放在 MERGE 语句里,跟新增数据一起处理。下面是一个典型的实现。
-- 假设源表为 src_customer,目标表为 dim_customer
-- 我们只对 email 字段做类型1覆盖
MERGE INTO dim_customer AS target
USING src_customer AS source
ON target.customer_id = source.customer_id
WHEN MATCHED AND (
target.email != source.email
OR target.phone != source.phone
) THEN
UPDATE SET
email = source.email,
phone = source.phone,
update_time = CURRENT_TIMESTAMP
WHEN NOT MATCHED THEN
INSERT (
customer_id,
name,
email,
phone,
create_time,
update_time
) VALUES (
source.customer_id,
source.name,
source.email,
source.phone,
CURRENT_TIMESTAMP,
CURRENT_TIMESTAMP
);
注意看 WHEN MATCHED AND 后面的条件——我只在值真正变化时才更新。为什么?因为不必要的更新会刷掉 update_time,让你搞不清到底什么时候改的。这个细节,很多教程不会告诉你。
什么时候该放弃类型1?
我给自己定了一个原则,分享给你参考:
- 如果这个字段的变更会影响历史报表的聚合结果 → 不要用类型1
- 如果业务方明确要求「可回溯」 → 不要用类型1
- 如果这个字段是核心分析维度(比如客户等级、产品分类) → 不要用类型1
- 如果源系统会保留变更日志 → 优先考虑类型2
说白了,类型1是个「省心但不省事」的方案。省心的是实现,不省事的是后续你可能要面对业务方的灵魂拷问——「为什么上个月的数据跟现在不一样了?」
一句话总结:SCD 类型1适合「错了就改、改了不追」的场景。用之前,先想清楚你能不能承受「失忆」的代价。