Databricks

Delta Change Data Feed Complete Guide: CDC, Incremental Processing, Downstream Integration

2026-03-21
Updated: 2026-03-27
NicheeLab Editorial Team

Change Data Feed (CDF) automatically records changes (INSERT / UPDATE / DELETE) to a Delta Table and efficiently propagates "what changed" to downstream pipelines and systems. It is commonly used for incremental processing between Silver and Gold layers, and for integration with external systems.

This article walks through enabling CDF, the meaning of each _change_type value, how to read CDF data in batch and streaming modes, the foreachBatch + MERGE pattern for downstream propagation, how CDF relates to Lakeflow Spark Declarative Pipelines APPLY CHANGES INTO, and the points the exam tends to ask about.

Enabling CDF

CDF is enabled per table. You can set it when creating a new table or by changing properties on an existing one. Only changes that occur after CDF is enabled are recorded — history from before enabling is not available.

-- Enable it when creating a new table
CREATE TABLE silver.orders (
  order_id BIGINT,
  customer_id BIGINT,
  amount DECIMAL(10,2),
  status STRING,
  updated_at TIMESTAMP
)
TBLPROPERTIES (delta.enableChangeDataFeed = true);

-- Enable it on an existing table
ALTER TABLE silver.orders
SET TBLPROPERTIES (delta.enableChangeDataFeed = true);

-- Enable it by default across the whole workspace
SET spark.databricks.delta.properties.defaults.enableChangeDataFeed = true;

The _change_type Values

Data read via CDF automatically includes three metadata columns. The most important one is _change_type, which indicates the kind of change.

_change_typeMeaningOperations that produce it
insertA new row was insertedINSERT, MERGE (NOT MATCHED THEN INSERT)
update_preimageThe state of the row before the updateUPDATE, MERGE (MATCHED THEN UPDATE)
update_postimageThe state of the row after the updateUPDATE, MERGE (MATCHED THEN UPDATE)
deleteThe row was deletedDELETE, MERGE (MATCHED THEN DELETE)

For UPDATEs, both a preimage (before) and a postimage (after) row are recorded. This ability to trace exactly "what changed into what" is CDF's strength. Two additional metadata columns are also added: _commit_version (the Delta version number) and _commit_timestamp (the commit time).

Reading from CDF

Batch read: the table_changes() function

-- Read changes over a range of versions
SELECT * FROM table_changes('silver.orders', 5, 10);

-- Read changes over a range of timestamps
SELECT * FROM table_changes('silver.orders', '2026-03-01', '2026-03-27');

-- Filter to a specific _change_type
SELECT * FROM table_changes('silver.orders', 5)
WHERE _change_type IN ('insert', 'update_postimage');

Streaming read: the readChangeFeed option

# Start from a given version
cdf_stream = (spark.readStream
  .format("delta")
  .option("readChangeFeed", "true")
  .option("startingVersion", 5)
  .table("silver.orders")
)

# Start from a given timestamp
cdf_stream = (spark.readStream
  .format("delta")
  .option("readChangeFeed", "true")
  .option("startingTimestamp", "2026-03-01")
  .table("silver.orders")
)

Incremental Propagation to Downstream Pipelines (foreachBatch + MERGE)

The most common use of CDF is incremental processing between Silver and Gold. Instead of fully rebuilding the Gold aggregate table every run, you fetch only the changes via CDF and apply them with MERGE.

def update_gold(batch_df, batch_id):
    updates = batch_df.filter(
        "_change_type IN ('insert', 'update_postimage')"
    )
    deletes = batch_df.filter("_change_type = 'delete'")

    updates.createOrReplaceTempView("updates")
    deletes.createOrReplaceTempView("deletes")

    # UPSERT: apply inserts and the post-update state
    spark.sql("""
      MERGE INTO gold.order_summary t
      USING updates s ON t.order_id = s.order_id
      WHEN MATCHED THEN UPDATE SET *
      WHEN NOT MATCHED THEN INSERT *
    """)

    # DELETE: remove deleted rows from Gold as well
    spark.sql("""
      MERGE INTO gold.order_summary t
      USING deletes s ON t.order_id = s.order_id
      WHEN MATCHED THEN DELETE
    """)

(spark.readStream
  .format("delta")
  .option("readChangeFeed", "true")
  .table("silver.orders")
  .writeStream
  .foreachBatch(update_gold)
  .option("checkpointLocation", "/checkpoint/gold_orders")
  .trigger(availableNow=True)
  .start()
)

trigger(availableNow=True) runs the stream in a batch-like fashion, processing all available CDF data and then stopping the stream automatically. It pairs well with scheduled jobs.

Integration with External Systems

CDF is also useful for incremental data integration with external systems. Sending only the changes instead of resending the full dataset dramatically reduces network and processing costs.

  • Incremental loads into Snowflake / Redshift
  • Index updates for Elasticsearch / OpenSearch
  • Cache updates for Redis / DynamoDB
  • Publishing change events to Kafka

Relationship with Lakeflow Spark Declarative Pipelines APPLY CHANGES INTO

Lakeflow Spark Declarative Pipelines APPLY CHANGES INTO is conceptually similar to CDF but lives at a different layer. CDF is a change-recording feature on the Delta Table side, while APPLY CHANGES INTO is a CDC-ingestion syntax on the Lakeflow Spark Declarative Pipelines pipeline side.

Comparison axisChange Data FeedLakeflow Spark Declarative Pipelines APPLY CHANGES INTO
RoleRecords and exposes changes to a Delta TableApplies changes from a CDC source into a Lakeflow Spark Declarative Pipelines table
How to enableTable propertyLakeflow Spark Declarative Pipelines pipeline definition (SQL/Python)
Key specificationNot required (every row's changes are recorded)Primary key specified via the KEYS clause
SequencingOrdered automatically by _commit_versionOrdering column specified via the SEQUENCE BY clause
SCD Type 2User implements the logicManaged automatically with STORED AS SCD TYPE 2
-- Lakeflow Spark Declarative Pipelines APPLY CHANGES INTO example
APPLY CHANGES INTO LIVE.silver_orders
FROM STREAM(LIVE.bronze_orders_cdc)
KEYS (order_id)
SEQUENCE BY updated_at
COLUMNS * EXCEPT (_rescued_data)
STORED AS SCD TYPE 1;

Retention and VACUUM

CDF data is stored in the Delta Table's _change_data directory. Running VACUUM also removes CDF data that falls outside the retention window.

-- Run VACUUM (7-day retention)
VACUUM silver.orders RETAIN 168 HOURS;

-- Use a longer retention period to allow for downstream pipeline lag
ALTER TABLE silver.orders
SET TBLPROPERTIES (
  'delta.deletedFileRetentionDuration' = 'interval 30 days'
);

Points the Exam Tests

  • How to enable CDF: TBLPROPERTIES (delta.enableChangeDataFeed = true)
  • The four _change_type values: insert / update_preimage / update_postimage / delete
  • Streaming reads via the readChangeFeed option
  • Batch reads via the table_changes() function
  • Changes from before CDF was enabled cannot be retrieved
  • UPDATEs record two rows: preimage and postimage
  • APPLY CHANGES INTO is Lakeflow Spark Declarative Pipelines pipeline syntax — a separate layer from CDF

Check Your Understanding

Data Engineer Professional

Question 1

CDF is enabled on the Silver orders table. A record's status is updated from 'pending' to 'shipped'. What rows will the data read from CDF contain?

  1. Two rows: _change_type='update_preimage' (status='pending') and _change_type='update_postimage' (status='shipped')
  2. A single row with _change_type='update' (status='shipped')
  3. Two rows: _change_type='delete' (status='pending') and _change_type='insert' (status='shipped')
  4. A single row with _change_type='insert' (status='shipped')

Correct answer: A

For an UPDATE operation, CDF records two rows: update_preimage (the state before) and update_postimage (the state after). This lets you fully trace what changed into what. There is no plain 'update' type, and CDF uses a preimage/postimage pair rather than a delete/insert combination.

Frequently Asked Questions

What is the difference between CDF and Structured Streaming?

Change Data Feed (CDF) is a feature that automatically records changes to a Delta Table, exposing the results of INSERT/UPDATE/DELETE operations together with the _change_type, _commit_version, and _commit_timestamp metadata columns. Structured Streaming is Spark's streaming framework and is one of several ways to consume CDF data. Think of CDF as the mechanism that records changes, and Streaming as one way to read them.

Does enabling CDF increase storage costs?

Yes, slightly. When UPDATE or DELETE runs on a CDF-enabled table, the before/after row data is stored separately under the _change_data directory. Tables that only see INSERTs incur almost no additional cost. VACUUM can clean up old CDF data, but you must keep the retention window that downstream pipelines depend on.

How does CDF show up on the exam?

It is especially frequent on Data Engineer Professional. Common topics include how to enable CDF (table property), reading via the readChangeFeed option, the meaning of each _change_type value (insert/update_preimage/update_postimage/delete), and patterns for applying changes incrementally to downstream tables. Batch reads via the table_changes() function are also important.

Check what you learned with practice questions

Practice with certification-focused question sets

Try free questions
Author

NicheeLab Editorial Team

NicheeLab editorial team focused on data engineering and cloud certification learning. Content is structured around practical study needs and official exam domains.


Related articles
Databricks

Databricks Certifications: All 7 Exams, Difficulty & Study Plan (2026)

Complete guide to all 7 Databricks certifications — Data Eng...

Databricks

Databricks Exam Difficulty Ranking: All 7 Certs Compared (2026)

Every Databricks certification ranked by difficulty, with st...

Databricks

Databricks Study Guide: Fastest Pass Route & Time Estimates (2026)

How to pass Databricks certifications efficiently. Official ...

Databricks

Databricks Data Engineer Associate: Complete Guide (2026)

Domain-by-domain breakdown of the Databricks Certified Data ...

Databricks

Databricks Data Engineer Professional: Complete Guide (2026)

Tactics for the Databricks Certified Data Engineer Professio...

Browse all Databricks articles (110)
© 2026 NicheeLab All rights reserved.