CertSafari
    Databricks Certified Data Engineer Professional - October 9 onwards· Lessons

    Domain 2 · Lesson 14/46

    AUTO CDC in Lakeflow Pipelines: Keys, Sequencing and SCD Types

    Configure incremental CDC pipelines using Lakeflow Pipelines with Delta or Iceberg as the target table format.

    13 min read
    2.4% of exam
    4 sources
    Published 2 Oct 2026
    Docs as of 30 Sep 2026

    What you will be able to do

    • Choose between AUTO CDC and AUTO CDC FROM SNAPSHOT based on what the source system provides, and name the pipeline editions that support them
    • Write an AUTO CDC flow in SQL or Python with KEYS, SEQUENCE BY, delete and truncate conditions, and a column list
    • Predict the contents of an SCD Type 1 and an SCD Type 2 target table from the same change feed, including late-arriving events
    • Configure tombstone retention, truncates and one-time backfill flows correctly

    Key concept

    Keyed, sequenced upsert — AUTO CDC treats every change record as an upsert or delete against the row identified by KEYS, and uses the SEQUENCE BY value, not arrival order, to decide which change wins. Without both, the pipeline cannot rebuild correct current state or history from a change feed.

    1.Which CDC API, and what the pipeline needs

    Change data capture usually starts outside Databricks. A tool such as Debezium reads a transactional database and emits one record per changed row. In the Databricks CDC tutorial, Debezium "typically sends the history of data changes to Kafka topics or saves them as files." Your Lakeflow pipeline then has to turn that stream of inserts, updates and deletes into a table that reflects the source.

    Lakeflow Pipelines offers two APIs for this, and the choice depends on what the source can provide. AUTO CDC is for sources that emit a change feed. AUTO CDC FROM SNAPSHOT is for sources that only give you full snapshots: it compares consecutive snapshots to work out what changed. AUTO CDC works in both the SQL and Python pipeline interfaces. FROM SNAPSHOT works only in Python.

    Choosing the CDC API
    APIUse whenInterfaces
    AUTO CDC (AUTO CDC ... INTO / create_auto_cdc_flow)The source database has a CDC feed enabledSQL and Python
    AUTO CDC FROM SNAPSHOT (create_auto_cdc_from_snapshot_flow)CDC is not enabled and only snapshots are availablePython only

    Two naming details come up on the exam. First, AUTO CDC is the current name for what used to be APPLY CHANGES: "The AUTO CDC APIs replace the APPLY CHANGES APIs and have the same syntax." The old APIs still work, but Databricks recommends the new names. In Python, apply_changes() became create_auto_cdc_flow(). Second, these APIs are a Databricks Lakeflow feature. They are not supported by open-source Apache Spark Declarative Pipelines.

    Checkpoint 1 of 6· Check yourself

    A source database has CDC disabled, and the team only has nightly full extracts. The team wants to write the whole pipeline in SQL. What is the problem?

    Sources12

    2.Anatomy of an AUTO CDC flow

    An AUTO CDC pipeline always has two parts. To set it up, you "create a streaming table and then use the AUTO CDC ... INTO statement in SQL or the create_auto_cdc_flow() function in Python". The streaming table is the target. The flow says how changes are applied to it. Here is the full SQL grammar:

    AUTO CDC INTO grammar: only KEYS and SEQUENCE BY are required clausessql
    CREATE OR REFRESH STREAMING TABLE table_name;
    
    CREATE FLOW flow_name AS AUTO CDC [ONCE] INTO table_name
    FROM source
    KEYS (keys)
    [IGNORE NULL UPDATES [ON {columnList | * EXCEPT (exceptColumnList)}]]
    [APPLY AS DELETE WHEN condition]
    [APPLY AS TRUNCATE WHEN condition]
    SEQUENCE BY orderByColumn
    [SYSTEM SEQUENCE BY systemOrderByColumn]
    [COLUMNS {columnList | * EXCEPT (exceptColumnList)}]
    [STORED AS {SCD TYPE 1 | SCD TYPE 2 | BITEMPORAL}]
    [TRACK HISTORY ON {columnList | * EXCEPT (exceptColumnList)}]
    [COLUMNS TO UPDATE columnName]

    KEYS names the column or columns that uniquely identify a row. By default every INSERT and UPDATE event is an upsert: AUTO CDC will "update any rows in the target table that match the specified key(s) or insert a new row". Deletes happen only when you add an APPLY AS DELETE WHEN condition.

    SEQUENCE BY gives the logical order of events. The column must be sortable: "Specified columns must be sortable data types. This clause is required." When one column can tie, such as two events with the same timestamp, wrap several columns in a STRUCT, for example SEQUENCE BY STRUCT(timestamp_col, id_col). The API compares the first field and only falls back to the next field on a tie.

    FROM must be a streaming source, so you use the stream(...) keyword. If the stream hits a change or deletion to an existing record, it throws an error, so "It is safest to read from static or append-only sources." **COLUMNS * EXCEPT keeps operation metadata, such as the operation code and sequence number, out of the target. STORED AS** selects the history model and defaults to SCD Type 1.

    Checkpoint 2 of 6· Fill the gap

    Which keyword completes the clause that orders change events in this SCD Type 1 flow?

    CREATE OR REFRESH STREAMING TABLE users_current;
    
    CREATE FLOW apply_cdc AS AUTO CDC INTO
      users_current
    FROM
      stream(main.cdc_tutorial.users_cdf)
    KEYS
      (userId)
    APPLY AS DELETE WHEN
      operation = "DELETE"
    APPLY AS TRUNCATE WHEN
      operation = "TRUNCATE"
     ?  BY
      sequenceNum
    COLUMNS * EXCEPT
      (operation, sequenceNum)
    STORED AS
      SCD TYPE 1;

    Sources13

    3.SCD Type 1 vs Type 2 on the same feed

    The Databricks examples run one small change feed through both history models. Notice that the two sequence-5 updates are listed after the sequence-6 events: they arrived late.

    Input change feed (users_cdf)
    userIdnamecityoperationsequenceNum
    124RaulOaxacaINSERT1
    123IsabelMonterreyINSERT1
    125MercedesTijuanaINSERT2
    126LilyCancunINSERT2
    123nullnullDELETE6
    125MercedesGuadalajaraUPDATE6
    125MercedesMexicaliUPDATE5
    123IsabelChihuahuaUPDATE5

    SCD Type 1 keeps only the latest version of each key. The result has three rows: 124 Raul/Oaxaca, 125 Mercedes/Guadalajara and 126 Lily/Cancun. Isabel was deleted, and Mercedes shows one city because "SCD Type 1 overwrites previous values."

    SCD Type 2 "preserves a complete history of changes by creating new rows for each version of a record". The pipeline adds __START_AT and __END_AT columns, filled from the sequencing values. Here is the Python version. The only differences from Type 1 are the stored_as_scd_type value and the missing truncate condition.

    SCD Type 2 flow in the Python pipeline interfacepython
    from pyspark import pipelines as dp
    from pyspark.sql.functions import col, expr
    
    @dp.view
    def users():
      return spark.readStream.table("main.cdc_tutorial.users_cdf")
    
    dp.create_streaming_table("users_history")
    
    dp.create_auto_cdc_flow(
      target = "users_history",
      source = "users",
      keys = ["userId"],
      sequence_by = col("sequenceNum"),
      apply_as_deletes = expr("operation = 'DELETE'"),
      except_column_list = ["operation", "sequenceNum"],
      stored_as_scd_type = "2"
    )
    SCD Type 2 result: open versions have __END_AT = null
    userIdcity__START_AT__END_AT
    123Monterrey15
    123Chihuahua56
    124Oaxaca1null
    125Tijuana25
    125Mexicali56
    125Guadalajara6null
    126Cancun2null

    Mercedes has three versions, including Mexicali, because SCD Type 2 places the late event at its sequence position instead of discarding it. Isabel's history ends at 6, when she was deleted. If you declare an explicit schema for an SCD Type 2 target, you must include __START_AT and __END_AT with the same data type as the sequence column.

    You don't have to version every column. TRACK HISTORY ON limits which columns create a new history row when they change:

    SCD Type 2 that ignores changes to city when deciding whether to cut a new versionsql
    CREATE FLOW flow
    AS AUTO CDC INTO
      target
    FROM stream(cdc_data.users)
      KEYS (userId)
      APPLY AS DELETE WHEN operation = "DELETE"
      SEQUENCE BY sequenceNum
      COLUMNS * EXCEPT (operation, sequenceNum)
      STORED AS SCD TYPE 2
      TRACK HISTORY ON * EXCEPT (city);

    A third option, STORED AS BITEMPORAL, tracks both business time and system time. It is in Beta and requires SYSTEM SEQUENCE BY.

    Checkpoint 3 of 6· Match them up

    Match each setting or column to its effect

    Tap a term, then the definition that fits it.

    Checkpoint 4 of 6· Exam question

    A bronze streaming table receives CDC events with columns `customer_id`, `op`, `updated_at`, and other attributes. Events sometimes arrive out of order because of network retries. An engineer defines: ```sql CREATE OR REFRESH STREAMING TABLE dim_customer; CREATE FLOW customer_cdc AS AUTO CDC INTO dim_customer FROM stream(bronze_customer_changes) KEYS (customer_id) STORED AS SCD TYPE 1; ``` During testing, an update carrying an earlier `updated_at` value overwrites a row that had already been updated with a later `updated_at`. Which change fixes this?

    Sources143

    4.Late events, deletes, truncates and backfills

    Deletes need special handling, because an older update could arrive after the delete. To prevent that update from bringing the row back, "the deleted row is temporarily retained as a tombstone in the underlying Delta table". A view in the metastore hides tombstones from readers. "The retention interval defaults to two days", and you can change it with the pipelines.cdc.tombstoneGCThresholdInSeconds table property.

    This matters most with Auto Loader. If Auto Loader reads your CDC files, keep in mind that "Auto Loader does not guarantee file processing order". The documentation's rule is to set the threshold "to a value that exceeds the maximum expected delay between event arrival and pipeline execution."

    Checkpoint 5 of 6· Check yourself

    CDC files land in cloud storage and are read with Auto Loader. Some DELETE events reach the pipeline up to four days late, and deleted customers occasionally reappear. What is the fix?

    APPLY AS TRUNCATE WHEN clears the whole target table when a matching event arrives. In the sample data, a TRUNCATE at sequence 3 leaves only Mercedes/Guadalajara, because her sequence-6 update is newer than the truncate. Truncation is a blunt tool, and "The APPLY AS TRUNCATE WHEN clause is supported only for SCD type 1."

    The last piece is the initial load. A change feed usually begins after the source table already holds data. "For initial hydration from a source with a change feed, use AUTO CDC with a once flow and then continue processing the change feed." Use AUTO CDC ONCE in SQL or once=True in Python. A once flow reads a batch DataFrame, "not a streaming DataFrame", and it runs again only after a full refresh.

    Checkpoint 6 of 6· Exam question

    A streaming table target receives events with an `op` column set to `'INSERT'`, `'UPDATE'`, or `'DELETE'`. After deploying: ```sql CREATE OR REFRESH STREAMING TABLE orders_silver; CREATE FLOW orders_cdc AS AUTO CDC INTO orders_silver FROM stream(bronze_orders_changes) KEYS (order_id) SEQUENCE BY event_ts STORED AS SCD TYPE 1; ``` rows marked as deletes in the source still appear in `orders_silver` after the flow runs. Which addition resolves this?

    Sources41

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.Any Lakeflow pipeline edition can run AUTO CDC.Why is that wrong?

      The CDC APIs need serverless pipelines or the Pro or Advanced edition.

      Covered in Which CDC API, and what the pipeline needs

    2. 2.APPLY AS TRUNCATE WHEN can clear an SCD Type 2 history table.Why is that wrong?

      Truncate handling is supported only for SCD Type 1. SCD Type 2 does not support truncate operations.

      Covered in Late events, deletes, truncates and backfills

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
      “The AUTO CDC APIs replace the APPLY CHANGES APIs and have the same syntax.”
      ↩︎ Which CDC API, and what the pipeline needs
      “The AUTO CDC APIs are not supported by Apache Spark Declarative Pipelines.”
      ↩︎ Which CDC API, and what the pipeline needs
      “create a streaming table and then use the AUTO CDC ... INTO statement in SQL or the create_auto_cdc_flow() function in Python”
      ↩︎ Anatomy of an AUTO CDC flow
      “The API orders by the first field first, and in the event of a tie, considers the second field, and so on.”
      ↩︎ Anatomy of an AUTO CDC flow
      “SCD Type 2 preserves a complete history of changes by creating new rows for each version of a record”
      ↩︎ SCD Type 1 vs Type 2 on the same feed
      “Records with __END_AT = null are currently active.”
      ↩︎ SCD Type 1 vs Type 2 on the same feed
      “For initial hydration from a source with a change feed, use AUTO CDC with a once flow and then continue processing the change feed.”
      ↩︎ Late events, deletes, truncates and backfills
      “your pipeline must be configured to use serverless Lakeflow pipelines or the Lakeflow pipelines Pro or Advanced editions.”
      ↩︎ Exam trap 1
      “your pipeline must be configured to use serverless Lakeflow pipelines or the Lakeflow pipelines Pro or Advanced editions.”
      ↩︎ Prediction
      “It is supported only in the Python pipeline interface.”
      ↩︎ Checkpoint
      “Use SCD Type 2 to retain a history of records, either on all updates or on updates to a specified set of columns.”
      ↩︎ Checkpoint
      “The earlier UPDATE at sequenceNum=5 was dropped because a later update at sequenceNum=6 arrived.”
      ↩︎ Prediction
    2. 2.
      “It typically sends the history of data changes to Kafka topics or saves them as files.”
      ↩︎ Which CDC API, and what the pipeline needs
    3. 3.
      “update any rows in the target table that match the specified key(s) or insert a new row”
      ↩︎ Anatomy of an AUTO CDC flow
      “Specified columns must be sortable data types. This clause is required.”
      ↩︎ Anatomy of an AUTO CDC flow
      “It is safest to read from static or append-only sources.”
      ↩︎ Anatomy of an AUTO CDC flow
      “Bitemporal requires SYSTEM SEQUENCE BY and is in Beta.”
      ↩︎ SCD Type 1 vs Type 2 on the same feed
      “The APPLY AS TRUNCATE WHEN clause is supported only for SCD type 1.”
      ↩︎ Exam trap 2
    4. 4.
      “you must include the __START_AT and __END_AT columns with the same data type as the sequence_by fields.”
      ↩︎ SCD Type 1 vs Type 2 on the same feed
      “the deleted row is temporarily retained as a tombstone in the underlying Delta table”
      ↩︎ Late events, deletes, truncates and backfills
      “Auto Loader does not guarantee file processing order”
      ↩︎ Late events, deletes, truncates and backfills
      “must be a batch DataFrame in this case, not a streaming DataFrame”
      ↩︎ Late events, deletes, truncates and backfills
      “Lakeflow pipelines use this sequencing to handle change events that arrive out of order.”
      ↩︎ Key concept
      “Set pipelines.cdc.tombstoneGCThresholdInSeconds to a value that exceeds the maximum expected delay between event arrival and pipeline execution.”
      ↩︎ Checkpoint

    Continue to page 2 of 2

    CDC Target Tables in Lakeflow Pipelines: Delta, Iceberg Reads, Snapshots and Partial Updates

    Spotted a mistake, or was something unclear? Tell us.