在Azure Databricks中实现缓慢变化维度(SCD)的三种类型

在Azure Databricks中使用PySpark实现缓慢变化维度(SCD)的三种核心类型,需结合Spark SQL和DataFrame API的特性,并利用Delta Lake的事务支持。以下是具体设计与实现步骤,以及测试用例:

通过以下步骤,可在Azure Databricks中高效实现SCD逻辑,确保数据历史可追溯且符合业务需求。


类型1:覆盖旧值(Overwrite Old Value)

设计要点
  • 直接更新目标表中变化的字段,不保留历史记录。
  • 使用MERGE INTO操作(Delta Lake特性)实现高效更新。
实现步骤
  1. 读取数据:加载源数据(增量数据)和维度表(目标表)。
  2. 匹配键 :根据业务键(如customer_id)匹配源数据和目标表。
  3. 更新记录:若匹配成功且属性变化,更新目标表字段。
  4. 写入结果 :使用MERGE操作完成更新。
代码示例
python 复制代码
from pyspark.sql import SparkSession
spark = SparkSession.builder.getOrCreate()

# 加载源数据和目标表
source_df = spark.read.format("delta").load("source_path")
target_df = spark.read.format("delta").load("target_path")

# 创建临时视图
source_df.createOrReplaceTempView("source")
target_df.createOrReplaceTempView("target")

# 执行MERGE操作
merge_sql = """
MERGE INTO target AS t
USING source AS s
ON t.customer_id = s.customer_id
WHEN MATCHED AND t.address <> s.address THEN
  UPDATE SET t.address = s.address
WHEN NOT MATCHED THEN
  INSERT (customer_id, name, address) 
  VALUES (s.customer_id, s.name, s.address)
"""

spark.sql(merge_sql)
测试用例
  • 场景1:客户地址变更,验证目标表中旧地址被覆盖。
  • 场景2:新增客户插入,验证记录正确添加。
  • 场景3:无变化数据,确认无操作执行。

类型2:创建新记录(SCD Type 2)

设计要点
  • 为每个变化生成新记录,维护start_date、end_date和is_current标志。
  • 使用窗口函数检测变化,MERGE操作插入新记录并更新旧记录状态。
实现步骤
  1. 添加版本标识 :在目标表中增加start_date、end_date和is_current字段。
  2. 检测变化:对比源数据与目标表,标记变化的记录。
  3. 关闭旧记录 :将变化记录的end_date设为当前日期,is_current设为False。
  4. 插入新记录 :为新记录设置start_date为当前时间,end_date为NULL,is_current为True。
代码示例
python 复制代码
from pyspark.sql import functions as F
from pyspark.sql.window import Window

# 加载数据
source = spark.read.format("delta").load("source_path")
target = spark.read.format("delta").load("target_path")

# 检测变化:假设通过last_updated_time判断
changes = source.join(target, "product_id", "left_outer") \
    .filter(
        (source.price != target.price) | 
        target.product_id.isNull()
    )

# 关闭旧记录
closed_records = target.join(changes, "product_id", "inner") \
    .withColumn("end_date", F.current_date()) \
    .withColumn("is_current", F.lit(False)) \
    .select(target["*"], "end_date", "is_current")

# 生成新记录(新增或变化)
new_records = changes.withColumn("start_date", F.current_date()) \
    .withColumn("end_date", F.lit(None).cast("date")) \
    .withColumn("is_current", F.lit(True)) \
    .select("product_id", "price", "start_date", "end_date", "is_current")

# 合并并写入Delta表
final_df = closed_records.unionByName(new_records)
final_df.write.format("delta") \
    .mode("overwrite") \
    .saveAsTable("product_dimension")
测试用例
  • 场景1 :产品价格首次插入,验证is_current=True且end_date=NULL。
  • 场景2 :价格变更后,旧记录is_current=False,新记录时间连续。
  • 场景3:多次变更,检查历史版本数量及时间连续性。

类型3:添加有效日期(SCD Type 3)

设计要点
  • 为每个记录维护valid_from和valid_to日期,仅保留有限历史(如最新一次变更)。
  • 更新时修改旧记录的valid_to,插入新记录。
实现步骤
  1. 初始化字段 :在目标表中添加valid_from和valid_to。
  2. 检测变化:对比新旧数据,找出属性变化的记录。
  3. 关闭旧记录 :将旧记录valid_to设为当前日期。
  4. 插入新记录 :设置新记录的valid_from为当前日期,valid_to为NULL。
代码示例
python 复制代码
# 加载数据
employee_source = spark.read.format("delta").load("source_path")
employee_target = spark.read.format("delta").load("target_path")

# 检测职位变化
changes = employee_source.join(employee_target, "employee_id", "left") \
    .filter(employee_source.position != employee_target.position)

# 关闭旧记录(设置valid_to)
closed = changes.select(employee_target["*"]) \
    .withColumn("valid_to", F.current_date())

# 插入新记录
new_records = changes.select(employee_source["*"]) \
    .withColumn("valid_from", F.current_date()) \
    .withColumn("valid_to", F.lit(None).cast("date"))

# 合并数据并写入
closed.unionByName(new_records) \
    .write.format("delta") \
    .mode("overwrite") \
    .saveAsTable("employee_dimension")
测试用例
  • 场景1 :员工职位首次记录,valid_to为空。
  • 场景2 :职位变更后,旧记录valid_to更新,新记录valid_from正确。
  • 场景3:查询特定时间点的职位状态,验证时间段准确性。

混合方法实现示例

结合类型1和类型2,对关键属性使用类型2,非关键属性使用类型1:

python 复制代码
# 关键属性(如地址)使用类型2
address_changes = detect_address_changes(source, target)
close_old_address_records(address_changes)
insert_new_address_records(address_changes)

# 非关键属性(如电话)使用类型1
phone_updates = detect_phone_changes(source, target)
update_phone_records(phone_updates)

关键优化点

  1. Delta Lake特性 :利用MERGE INTO、ACID事务、Z-Order优化查询性能。
  2. 数据版本管理 :使用DESCRIBE HISTORY查看SCD操作历史。
  3. 增量处理:仅处理变化的数据分区,减少计算量。
相关推荐
王中阳Go15 分钟前
两个入口答案不一致先别调模型:6 行入口口径对账表(附脚本)
人工智能·后端·python·大语言模型·1024程序员节
BK贝壳27 分钟前
bool-hybrid-array 落盘我只认 .bha:用 Create_BHA / Ask_BHA 做布尔数组序列化
数据结构·python·numpy·1024程序员节
Sakura_j_h_1 小时前
Python 在定时任务里输出 emoji,UnicodeEncodeError 从哪来
开发语言·字符编码·windows·python·定时任务
geovindu1 小时前
sql:SQL Pagination and Limiting Results using PorstgreSQL 18.0
大数据·数据仓库·sql·postgresql·数据库开发·数据库架构·1024程序员节
supabc1232 小时前
欧盟电子发票(XRechnung/Peppol/ZUGFeRD)踩坑记:用 Python 不装 Java 跑官方校验规则
xml·python·开源·电子发票·1024程序员节
ZhengEnCi3 小时前
AI Agent工具调用防护层怎么写-一份能直接跑的Python四层拦截实现
python·llm·agent
ZhengEnCi3 小时前
AI Agent工具误调用怎么解决-从参数校验到高危二次确认的四层工程防线
python·llm·agent
镜子AI4 小时前
镜子AI工程教程:用Python实现关键词布局检查器与语义改写查重(附完整代码)
人工智能·python·microsoft
小oo呆5 小时前
【学习心得】Python协程
python
信誓旦旦的程序猿5 小时前
【Python量化实战宝典 #22】涨跌停、连板情绪手册:情绪指标构建(Python 可跑)
python·1024程序员节·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口