Databricks DE Β· Topic 5.4
Apply a change feed to build slowly changing dimensions declaratively. SCD1 overwrites; SCD2 keeps versioned history with start/end timestamps. (Replaces APPLY CHANGES INTO.) This deep dive walks each with the code.
KEYS + SEQUENCE BY, STORED AS SCD TYPE 1 (overwrite) or TYPE 2 (history with __START_AT/__END_AT). It replaces APPLY CHANGES INTO.βΈ pick a type, then apply an address change for customer 7
{id:7, city:"Austin", ts:2026-06-20} already applied. Now apply {id:7, city:"Denver", ts:2026-06-24}:KEYS = the unique business key; SEQUENCE BY = a sortable column whose highest value wins regardless of arrival order β that's what makes AUTO CDC robust to late/out-of-order events vs a naive MERGE.
| SCD Type 1 | SCD Type 2 | |
|---|---|---|
| History | none (overwrite) | full (versioned rows) |
| Extra columns | β | __START_AT, __END_AT |
| Use for | "current value only" dims | audited / point-in-time joins |
| Table growth | flat | grows (row per change per key) |
CREATE OR REFRESH STREAMING TABLE dim_customers; CREATE FLOW cdc_flow AS AUTO CDC INTO dim_customers FROM stream(customers_changes) KEYS (customer_id) APPLY AS DELETE WHEN operation = 'DELETE' -- handle deletes explicitly SEQUENCE BY change_ts -- ordering; late events resequenced COLUMNS * EXCEPT (operation) STORED AS SCD TYPE 2 -- or SCD TYPE 1 (overwrite) TRACK HISTORY ON * EXCEPT (last_seen_ts); -- skip versioning noisy cols
from pyspark import pipelines as dp from pyspark.sql.functions import col, expr dp.create_streaming_table("dim_customers") dp.create_auto_cdc_flow( # replaces legacy dlt.apply_changes target="dim_customers", source="customers_changes", keys=["customer_id"], sequence_by=col("change_ts"), stored_as_scd_type=2, # 1 or 2 apply_as_deletes=expr("operation = 'DELETE'"))
Sources that arrive as full snapshots (Python-only)
dp.create_auto_cdc_from_snapshot_flow(
target="dim_customers", source="daily_snapshot",
keys=["customer_id"], stored_as_scd_type=2)
Read pipeline configuration values (set in pipeline settings or an Asset Bundle) at runtime so one pipeline serves many environments.
# pipeline setting: {"source_path": "/Volumes/main/raw/customers"} src = spark.conf.get("source_path") # read the parameter @dp.table def bronze_customers(): return spark.readStream.format("cloudFiles").option("cloudFiles.format","json").load(src)
-- in SQL, reference a pipeline config value with ${...} CREATE OR REFRESH STREAMING TABLE bronze_customers AS SELECT * FROM STREAM read_files('${source_path}', format => 'json');
dim_customers as SCD2 β every address change is versioned with start/end timestamps, so historical orders join to the address current at the time.create_auto_cdc_from_snapshot_flow to derive changes by comparing snapshots; parametrized pipelines for multi-env reuse.SEQUENCE BY must be sortable (else wrong winner on out-of-order data); deletes need APPLY AS DELETE WHEN or they're missed; TRACK HISTORY ON * versions on every column change β exclude noisy columns.FROM SNAPSHOT is Python-only.SEQUENCE BY β late events overwrite newer data.APPLY AS DELETE WHEN β deletes in the feed are ignored.dlt.apply_changes β prefer AUTO CDC / create_auto_cdc_flow.