Memory Atlas Β· Data processing

Delta Lake

Build a durable mental model of how Spark plans, moves, and optimizes distributed workβ€”then retrieve it under interview pressure.

Chapters
07
Advanced
05
Mode
Recall

Read for structure. Pause at each memory map and answer before revealing the detail.

Foundation

Delta Lake Overview and Interview Atlas

#

Delta Lake Overview and Interview Atlas

Answer First: Delta Lake is an open table format and transaction layer that records table state in a transaction log while storing data in data files such as Parquet.

Memory Map: data files + transaction log -> snapshot -> reliable table operations.

Version and product-name guardrails

  • Separate open-source Delta Lake behavior from Databricks-only SQL commands and managed services. Check the Delta Lake release index and the target runtime before making a version claim.
  • A checkpoint every 10 commits is a common default, not a protocol guarantee: delta.checkpointInterval is configurable, and connector applications decide when to checkpoint: official Delta Kernel guide.
  • Time travel does not preserve files independently of retention. VACUUM can remove files needed by old snapshots or long-running readers; inspect history and active workloads before shortening retention.
  • Liquid clustering is incremental because OPTIMIZE rewrites only data needed for clustering; it does not mean β€œnew data only”. It is incompatible with partitioning and ZORDER, and runtime support is versioned: official guide.
  • β€œStatistics on the first 32 columns” is the current default for Unity Catalog external tables, not a universal Delta limit. Managed-table statistics can be selected by predictive optimization, and explicit statistics columns are configurable: official guide.
  • Protocol, table-feature, CDF, deletion-vector, UniForm, clustering, and schema-evolution support is version-sensitive. Validate reader and writer compatibility before enabling a feature.
  • Performance percentages and file-size targets in the legacy notes are workload hypotheses, not guarantees; verify with table history, operation metrics, query plans, and representative benchmarks.

Canonical Delta Lake module map

Use the chapter navigation in order, or jump from the link-only question index to the exact concept owner.

Delta Lake & Lakehouse Architecture

Focus: Transaction log, MERGE scenarios, optimization, architecture decisions Complete Delta Guide: For the full standalone Delta Lake page (32 questions), see /learn/delta

Memory Map

🧠 T β†’ Transaction Log (the brain β€” _delta_log/)
DELTA & LAKEHOUSE→TACOVS-L
─────────────────────────────
TTransaction Log (the brain β€” _delta_log/)
AACID (Atomicity, Consistency, Isolation, Durability)
CCommands (MERGE, OPTIMIZE, VACUUM, Z-ORDER)
OOptimization (data skipping, file statistics, compaction)
VVersioning (Time Travel, RESTORE, DESCRIBE HISTORY)
SSchema (enforcement, evolution, column mapping)
LLakehouse (Bronze β†’ Silver β†’ Gold, open format, unified)

Answer First: A managed table uses a platform-managed storage location and lifecycle, while an external table registers data at a user-controlled location. Dropping the registration therefore has different data-retention consequences.

Memory Map: the difference between managed and external tables in Databricks -> storage owner defines responsibility -> registration chooses managed or external lifecycle -> DROP and retention behavior follow ownership -> location metadata proves the boundary [03_Delta_Lake_and_Lakehouse.md:596].

Q19: What is the difference between managed and external tables in Databricks?

Answer:

AspectManaged TableExternal Table
StorageDatabricks-managed locationUser-specified external location
DROP TABLEDeletes both metadata AND dataDeletes only metadata β€” data survives
Use caseDefault for most tablesShared data, data must survive table drops
Unity CatalogManaged by metastoreRequires External Location grant
sql
-- Managed (data stored in default warehouse location)
CREATE TABLE managed_orders (id LONG, amount DECIMAL);

-- External (data at your specified location)
CREATE TABLE external_orders (id LONG, amount DECIMAL)
LOCATION 's3://my-bucket/orders/';

SECTION 5: LAKEHOUSE ARCHITECTURE

Answer First: A lakehouse keeps open object storage and adds ACID transaction metadata, governance, and warehouse-style performance. Unlike a raw data lake it provides reliable table semantics, while unlike a traditional warehouse it preserves open files and separates storage from compute.

Memory Map: a data lakehouse? How does it differ from data lake and data warehouse -> data lakehouse it differ from data lake and data warehouse sets the specific transaction and interoperability constraint -> engine ecosystem defines interoperability needs -> mutation model selects transaction semantics -> maintenance ownership sets operating cost -> cross-engine test determines fit [03_Delta_Lake_and_Lakehouse.md:677].

Q22: What is a data lakehouse? How does it differ from data lake and data warehouse?

Simple Explanation:

  • Data Lake = Big messy warehouse (cheap rent, hard to find things, no ACID)
  • Data Warehouse = Expensive organized office (everything in place, but high rent, vendor lock-in)
  • Data Lakehouse = Organized warehouse (cheap rent + everything labeled and easy to find + ACID + open format)

The Lakehouse combines the BEST of both: cheap object storage (like a lake) + ACID transactions and fast BI queries (like a warehouse).

Answer:

AspectData LakeData WarehouseData Lakehouse
StorageCheap object storageProprietaryCheap object storage
FormatOpen (Parquet, ORC)ProprietaryOpen (Delta, Iceberg, Hudi)
ACIDNoYesYes
SchemaSchema-on-readSchema-on-writeBoth
PerformanceSlow for BIFast for BIFast (OPTIMIZE, caching, Photon)
ML supportGoodPoorExcellent
GovernanceLimitedStrongStrong (Unity Catalog)
CostLowHighLow-Medium

Answer First: Delta Lake / Iceberg / Hudi: ACID transactions on data lakes.

Memory Map: What key technologies enable the lakehouse -> open object storage preserves interoperable files -> transaction metadata adds reliable mutation -> scalable compute serves SQL and ML -> centralized governance joins both workloads [03_Delta_Lake_and_Lakehouse.md:700].

Q23: What key technologies enable the lakehouse?

Answer:

  1. Delta Lake / Iceberg / Hudi: ACID transactions on data lakes
  2. Photon / vectorized engines: Warehouse-level query performance
  3. Unity Catalog: Unified governance across all data assets
  4. Serverless compute: On-demand, auto-scaling
  5. SQL endpoints / SQL Warehouses: Direct BI tool connectivity
  6. MLflow: Integrated ML lifecycle management

Answer First: Eliminates the "two-tier" architecture (data lake + data warehouse).

Memory Map: What problems does the lakehouse solve -> single governed copy removes lake-to-warehouse duplication -> reliable tables support updates and concurrency -> independent compute serves varied workloads -> shared lineage reduces operational fragmentation [03_Delta_Lake_and_Lakehouse.md:712].

Q24: What problems does the lakehouse solve?

Answer:

  1. Eliminates the "two-tier" architecture (data lake + data warehouse)
    • No more ETL from lake to warehouse
    • Single copy of data serves all workloads
  2. Reduces data duplication and ETL complexity
  3. Single source of truth for BI and ML
  4. Open formats prevent vendor lock-in
  5. Cost-effective storage with warehouse-level performance
  6. Unified governance across all workloads

Interview Tip: "When asked 'Why Databricks over Snowflake?', mention: open formats (no vendor lock-in), unified BI + ML on one platform, Delta Lake is open source, and Photon gives warehouse-level speed on open data."

What NOT to Say: "Lakehouse means storing everything in one table" β€” No, it's an ARCHITECTURE pattern (Bronze/Silver/Gold layers) on cheap storage with ACID.

Answer First: DataFrame overwrite replaces table data within the write scope, whereas REPLACE TABLE atomically replaces the table definition and data. The required schema and metadata transition determine which operation is correct.

Memory Map: the difference between DataFrame.write.mode("overwrite") and REPLACE TABLE in Delta -> difference between dataframe write mode overwrite and replace table in delta applies a distinct transaction-log action set -> starting table version defines state -> metadata and file actions apply the operation -> atomic commit publishes a new snapshot -> history and metrics verify the transition [03_Delta_Lake_and_Lakehouse.md:818].

Q28: What is the difference between DataFrame.write.mode("overwrite") and REPLACE TABLE in Delta?

Answer:

Aspectmode("overwrite")REPLACE TABLE
ScopeOverwrites data (optionally per partition)Replaces entire table definition
SchemaKeeps existing schema (unless overwriteSchema=true)Can change schema
HistoryMaintains history (time travel works)Maintains history
Partition overwriteSupports replaceWhere for surgical overwritesN/A
python β€” editable
# Overwrite specific partitions only
df.write.format("delta") \
    .mode("overwrite") \
    .option("replaceWhere", "date = '2025-01-15'") \
    .saveAsTable("orders")

# This is IDEMPOTENT β€” safe for retry/re-run

Answer First: WAP ensures data quality before making data visible to consumers.

Memory Map: you implement write-audit-publish (WAP) pattern with Delta Lake -> writer creates an isolated candidate table -> validation checks quality and reconciliation -> atomic promotion changes the consumer pointer -> failed checks leave published state untouched [03_Delta_Lake_and_Lakehouse.md:840].

Q29: How do you implement write-audit-publish (WAP) pattern with Delta Lake?

Answer: WAP ensures data quality before making data visible to consumers.

python β€” editable
# Step 1: Write to a staging area (or use table clones)
staging = "staging_orders"
df.write.format("delta").mode("overwrite").saveAsTable(staging)

# Step 2: Audit β€” run quality checks
quality_check = spark.sql(f"""
    SELECT
        COUNT(*) as total_rows,
        SUM(CASE WHEN order_id IS NULL THEN 1 ELSE 0 END) as null_ids,
        SUM(CASE WHEN amount < 0 THEN 1 ELSE 0 END) as negative_amounts
    FROM {staging}
""").collect()[0]

assert quality_check["null_ids"] == 0, "Null order IDs found!"
assert quality_check["negative_amounts"] == 0, "Negative amounts found!"

# Step 3: Publish β€” atomically swap
spark.sql(f"""
    INSERT OVERWRITE TABLE production_orders
    SELECT * FROM {staging}
""")

Alternative with Delta's RESTORE: If quality check fails after writing to production, RESTORE TABLE production_orders TO VERSION AS OF .

Delta Lake & Lakehouse Deep Dive

πŸ’‘ Interview Tip
Time: 6-7 hours | Priority: HIGHEST β€” Delta Lake is 30-40% of any Databricks interview Context: Travel booking tables with billions of rows, fare pricing history, passenger PII Approach: Every topic starts with simple explanation β†’ then interview-level depth

Answer First: MERGE must resolve at most one source row for each target row that an update or delete clause changes. Deduplicate the source deterministically by business key and ordering column before the match.

Memory Map: What happens when source has duplicate keys? How to fix -> multiple changes for one target make mutation ambiguous -> deterministic ordering ranks candidate rows -> deduplication retains one winner per key -> uniqueness assertion runs before commit [DB_01_Delta_Lake_Deep_Dive.md:187].

Q6: What happens when source has duplicate keys? How to fix?

Simple Explanation: MERGE requires that each target row matches at most ONE source row. If your source data has duplicate booking_ids (e.g., two records for booking ABC123), MERGE doesn't know which one to use β†’ it throws an error.

The fix: Deduplicate the source data BEFORE merging. Keep only the latest record per key.

sql
-- Problem: bookings_staging has 2 rows for booking_id = 'ABC123'
-- Solution: Use ROW_NUMBER to keep only the latest one

WITH deduped AS (
    SELECT *,
        ROW_NUMBER() OVER (
            PARTITION BY booking_id        -- Group by booking_id
            ORDER BY updated_at DESC       -- Latest record first
        ) AS rn                            -- rn=1 means the latest record
    FROM bookings_staging
)
SELECT * FROM deduped WHERE rn = 1         -- Keep only the latest record per booking

Why duplicates happen in real life:

  • Source system sent the same event twice (retry)
  • Multiple Kafka partitions delivered the same record
  • File was reprocessed accidentally

SECTION 5: LAKEHOUSE ARCHITECTURE (30 min)

Q15: What is a Data Lakehouse? How is it different from Data Lake and Data Warehouse?

Simple Explanation:

Data Lake = Cheap cloud storage (like ADLS Gen2 or S3) where you dump all your raw data in any format (JSON, CSV, Parquet). It's cheap and flexible, but messy β€” no ACID transactions, no schema enforcement, slow for BI queries.

Data Warehouse = Expensive, structured database (like Azure Synapse, Snowflake) optimized for BI queries. Fast and well-governed, but expensive and bad for ML/unstructured data.

Data Lakehouse = The BEST of both. It takes the cheap storage of a data lake, adds Delta Lake for ACID transactions and schema enforcement, adds Photon for fast BI queries, and adds Unity Catalog for governance. You get data lake flexibility + data warehouse reliability at data lake prices.

Real-world analogy:

  • Data Lake = Big messy warehouse (cheap rent, hard to find things)
  • Data Warehouse = Expensive organized office (everything in place, but high rent)
  • Lakehouse = Organized warehouse (cheap rent + everything labeled and easy to find)
AspectData LakeData WarehouseLakehouse
Storage costCheap (ADLS/S3)Expensive (proprietary)Cheap (ADLS/S3)
File formatOpen (Parquet, JSON)Proprietary (locked in)Open (Delta, Iceberg)
ACID transactionsNo (data can get corrupted)YesYes (Delta Lake)
SchemaSchema-on-read (messy)Schema-on-write (strict)Both (flexible)
BI query speedSlowFastFast (Photon engine)
ML supportGoodPoorExcellent
GovernanceLimitedStrongStrong (Unity Catalog)

Q16: What technologies make the Lakehouse possible?

These are the key building blocks β€” know what each one does:

  1. Delta Lake β†’ Adds ACID transactions to cloud storage (the foundation of lakehouse)
  2. Photon Engine β†’ C++ query engine that makes queries as fast as a data warehouse (see Day 3)
  3. Unity Catalog β†’ Centralized governance β€” who can access what data (see Day 3)
  4. Serverless SQL Warehouses β†’ BI tools (Power BI, Tableau) connect directly to Databricks
  5. MLflow β†’ Manage the ML lifecycle (track experiments, deploy models)
  6. Lakeflow Declarative Pipelines β†’ Build ETL pipelines with built-in data quality checks (see Day 2)

Interview tip: When asked "Why Databricks over Snowflake?", mention: open formats (no vendor lock-in), unified BI + ML on one platform, Delta Lake is open source, and Photon gives warehouse-level speed on open data.

SECTION 6: NEW 2025-2026 FEATURES (30 min)

Answer First: Delta 4.x adds capabilities such as Variant for semi-structured values, type widening, and newer protocol features. Each feature must be checked against the Spark, Java, reader, and writer compatibility boundary before adoption.

Memory Map: What's new in Delta Lake 4.x? (Mention 2-3 in interview to show you're up to date) -> Variant stores semi-structured values under protocol support -> widening permits selected safe type changes -> feature adoption may raise reader or writer requirements -> compatibility matrix gates rollout [DB_01_Delta_Lake_Deep_Dive.md:655].

Q17: What's new in Delta Lake 4.x? (Mention 2-3 in interview to show you're up to date)

FeatureVersionSimple Explanation
Variant Data Type4.0Store messy JSON data without defining a schema first. Useful when source sends unpredictable JSON structures.
Type Widening4.0Change a column type (e.g., INT β†’ BIGINT) without rewriting all data files. Before this, you had to recreate the table!
Coordinated Commits4.0Multiple writers from different systems can write to the same table safely. Useful for multi-cloud setups.
Delta Connect4.0Do Delta operations (MERGE, etc.) remotely over Spark Connect β€” no need to run on the same cluster.
Conflict-Free Deletion Vectors4.1Enable deletion vectors on a table without blocking other writers. Before, enabling DV required exclusive access.
Server-Side Planning4.1Query planning done by the catalog server instead of the client β€” faster startup for large tables.
Atomic CTAS4.1CREATE TABLE AS SELECT is now fully atomic β€” if it fails midway, no partial table is left behind.

Breaking change: Delta 4.x needs Spark 4.x and Java 17+ (older versions won't work).

Interview tip: Mention Variant Data Type and Type Widening β€” they solve real problems that interviewers care about.

Answer First: Lakebase is a brand NEW feature (GA on Azure March 2026). It's a serverless PostgreSQL-compatible database built into Databricks.

Memory Map: Lakebase -> managed PostgreSQL service handles low-latency row transactions -> application reads and writes operational state -> governed integration exposes data to analytics -> replication lag measures freshness [DB_01_Delta_Lake_Deep_Dive.md:692].

Q19: What is Lakebase?

Simple Explanation: Lakebase is a brand NEW feature (GA on Azure March 2026). It's a serverless PostgreSQL-compatible database built into Databricks.

Why does it exist? Delta Lake is great for analytics (batch queries, BI), but NOT great for low-latency application queries (like "get this passenger's details in 10 ms for the mobile app"). Lakebase fills this gap β€” it's a real database for application use cases, running inside Databricks.

Key features:

  • Scale-to-zero: When nobody is querying, it costs $0 (shuts down automatically)
  • Database branching: Create an instant copy of your database for testing (like git branch for databases!)
  • Instant restore: Go back to any point in time if something goes wrong
  • Auto-failover HA: If one server fails, another takes over automatically

When to use Lakebase vs Delta tables:

Use CaseLakebaseDelta Table
App backend (API serving, low-latency lookups)βœ… Best choice❌ Too slow
Batch analytics (BI, reporting)❌ Not designed for thisβœ… Best choice
Feature serving for ML modelsβœ… Good (low-latency)βœ… Good (batch)
Application backend (CRUD operations)βœ… Best choice❌ Not designed for this

Q20: What is the difference between Managed and External tables?

Simple Explanation: When you create a table in Unity Catalog, you choose where the data is stored:

  • Managed Table: Databricks decides where to store the data (in a Databricks-managed location). If you DROP the table, BOTH the metadata AND the data are deleted. Simple and recommended for most cases.

  • External Table: YOU specify where the data lives (e.g., a specific ADLS Gen2 path). If you DROP the table, only the metadata is removed β€” the actual data files survive. Use this when data must persist even if the table definition is removed, or when data is shared with other systems.

Real-world analogy:

  • Managed = Renting a furnished apartment. If you end the lease, furniture goes too.
  • External = Renting an empty apartment and bringing your own furniture. If you end the lease, you take your furniture with you.
AspectManaged TableExternal Table
Where data livesDatabricks-managed location (auto)Your ADLS Gen2 path (you specify)
DROP TABLEDeletes metadata AND dataDeletes metadata ONLY β€” data survives
Predictive Optimizationβœ… Works automatically❌ Not supported
Best forMost tables (default choice)Data shared with other systems, legacy data
GovernanceFull Unity Catalog governanceNeeds External Location + Storage Credential setup
sql
-- Managed table (recommended for new tables β€” simpler to manage)
CREATE TABLE travel_catalog.bookings.flights (
    flight_id LONG,                    -- Unique flight identifier
    departure STRING,                  -- Departure airport code (e.g., BLR)
    arrival STRING                     -- Arrival airport code (e.g., DEL)
);
-- Databricks stores data in its managed location automatically
-- DROP TABLE will delete everything

-- External table (for data that must survive table drops)
CREATE TABLE travel_catalog.bookings.legacy_flights (
    flight_id LONG,
    departure STRING,
    arrival STRING
) LOCATION 'abfss://container@storage.dfs.core.windows.net/legacy/flights/';
-- Data lives at YOUR ADLS Gen2 path
-- DROP TABLE only removes the table definition, data files stay in ADLS

Example use case: "We use managed tables for new Delta tables (Predictive Optimization works automatically). We use external tables for legacy data migrated from Oracle that other systems also read."

Delta Lake & Lakehouse β€” Quick Recall

πŸ—ΊοΈ Memory Map
How to use this file:
  • ⚑ = Must remember (95% chance of being asked)
  • πŸ”‘ = Key concept (core understanding needed)
  • ⚠️ = Common trap (interviewers love to test this)
  • 🧠 = Memory Map (mnemonic/acronym β€” memorize this!)
  • πŸ“ = One-liner (flash-card style β€” cover answer, test yourself)
Reading strategy: Read Memory Maps FIRST β†’ then Direct Questions β†’ then Mid-Level. Memory Maps give you the skeleton. Questions fill in the details.

🧠 MASTER MEMORY MAP β€” Day 1

🧠 DELTA LAKE = "ACTS on your Data Lake"
DELTA LAKE"ACTS on your Data Lake"
AACID transactions (Atomicity, Consistency, Isolation, Durability)
CCheckpoints (summary every 10 commits)
TTransaction log (_delta_log/ folder = the brain)
SSchema enforcement (rejects bad data)
File Performance = "OVZ→LC" (Old Way → New Way)
OOPTIMIZE (compacts small files into big files)
VVACUUM (deletes old unused files)
ZZ-ORDER (sorts data for fast lookups β€” OLD way)
β†’
LLiquid Clustering (REPLACES partitioning + Z-ORDER β€” NEW way)
CClustering keys (the columns you cluster by)
Time Travel = "VTA"
VVersion number (VERSION AS OF 5)
TTimestamp (TIMESTAMP AS OF '2026-03-20')
AAction to recover (RESTORE TABLE ... TO VERSION AS OF)
Lakehouse = "Lake + Warehouse = Best of Both"
Lake→cheap storage, any format, schema-on-read
Warehouse→ACID, SQL, schema enforcement, fast queries
Lakehouse→all of the above on ONE platform

⚑ MUST KNOW DIRECT QUESTIONS (Cover the answer, test yourself!)

Q1What is Delta Lake?

An open-source storage layer that adds ACID transactions, schema enforcement, and time travel on top of Parquet files in a data lake (like ADLS Gen2).

Q2Where does Delta Lake store its metadata?

In the _delta_log/ folder β€” a series of JSON commit files + Parquet checkpoint files.

Q3What is the transaction log?

A folder (_delta_log/) that records every change as a numbered JSON file. It's the single source of truth for the table's current state.

Q4What are the 4 properties of ACID?

Atomicity (all or nothing), Consistency (schema rules enforced), Isolation (readers don't see partial writes), Durability (committed data survives crashes).

Q5What format are Delta data files stored in?

Parquet format. Delta Lake = Parquet files + transaction log. The log is what makes it "Delta."

Q6What is a checkpoint file?

A Parquet summary of the table state, created every 10 commits. Instead of reading 10,000 JSON files, read 1 checkpoint + recent JSONs.

Q7What is _last_checkpoint?

A small file that tells Delta which checkpoint is the latest β€” so it doesn't have to scan the entire _delta_log/ folder.

Q8What is snapshot isolation?

When you start reading a table, you see it as it was at that exact moment β€” even if someone writes new data while you're reading. You always get a consistent view.

Q9What is optimistic concurrency control?
βœ… Pro Tip
Delta assumes no conflict, lets multiple writers work in parallel, and checks for conflicts only at commit time. If conflict β†’ retry automatically.
⚠️ Q10When does optimistic concurrency FAIL (conflict)?

When two writers modify the same files. Example: Writer A and B both UPDATE rows in the same partition β†’ one gets a ConcurrentModificationException and must retry.

Q11What is data skipping?

Delta stores min/max statistics for each data file. When you query WHERE booking_date = '2026-03-15', Delta skips files where min > March 15 or max < March 15. Reads only relevant files.

Q12How many columns have statistics by default?

First 32 columns. Configurable via delta.dataSkippingNumIndexedCols. ⚠️ Put your most-filtered columns FIRST in schema!

πŸ”‘ MID-LEVEL QUESTIONS

Q13How does Delta read a table? (Step by step)

  1. Read _last_checkpoint β†’ find latest checkpoint
  2. Read that checkpoint file (Parquet) β†’ get base state
  3. Read all JSON commits AFTER the checkpoint β†’ apply recent changes
  4. Result: current list of valid Parquet data files
  5. Read only those Parquet files β†’ return query results

Q14What happens internally when you INSERT data?

  1. Spark writes new Parquet file(s) to the table folder
  2. Delta creates a new JSON commit in _delta_log/ with "add" action pointing to the new file(s)
  3. The commit is atomic β€” either the JSON file is fully written, or it's not

Q15What happens internally when you DELETE data?

  1. Delta identifies which Parquet files contain the rows to delete
  2. Reads those files, removes matching rows, writes NEW Parquet files with remaining rows
  3. Creates a commit with "remove" (old files) + "add" (new files)
  4. Old files are NOT physically deleted β€” they stay until VACUUM cleans them

⚠️ Q16Does DELETE physically remove files?

NO! DELETE only marks files as "removed" in the log. The physical Parquet files remain on disk. You need VACUUM to physically delete them. This is why time travel works β€” old files are still there.

Q17How does Delta handle concurrent writes? (Conflict resolution)

  • Two writers start at same snapshot (version 5)
  • Writer A commits first β†’ version 6 (success)
  • Writer B tries to commit β†’ Delta checks: "Did A modify the same files I'm modifying?"
    • If NO conflict (different files/partitions) β†’ auto-resolve, Writer B becomes version 7
    • If YES conflict (same files) β†’ ConcurrentModificationException β†’ retry from version 6

Q18What are the 4 types of actions in a commit JSON?

ActionMeaning
addNew Parquet file added
removeParquet file logically removed
metaDataSchema or table properties changed
commitInfoWho, when, what operation

⚑ MUST KNOW DIRECT QUESTIONS

Q19What is MERGE INTO?

A single SQL command that does INSERT + UPDATE + DELETE in one atomic operation. Also called "upsert" (update + insert).

Q20What is an upsert?

Update existing rows if they match, Insert new rows if they don't. MERGE does this in a single pass.

Q21Can MERGE also DELETE rows?

Yes! Add WHEN MATCHED AND source.deleted = true THEN DELETE β€” three operations in one command.

⚠️ Q22What happens if the source has duplicate keys in MERGE?
βœ… Pro Tip
Error! Delta throws UnsupportedOperationException β€” "Cannot perform MERGE as multiple source rows matched." You MUST deduplicate source first.
Q23How to fix duplicate keys in source?

Use ROW_NUMBER() window function to keep only the latest record per key:

sql
WITH deduped AS (
  SELECT *, ROW_NUMBER() OVER (
    PARTITION BY booking_id ORDER BY updated_at DESC
  ) AS rn FROM source_data
)
SELECT * FROM deduped WHERE rn = 1

Q24What is schema evolution in MERGE?

When source has NEW columns that target doesn't have, MERGE can auto-add them. Enable with: SET spark.databricks.delta.schema.autoMerge.enabled = true Or: .option("mergeSchema", "true")

πŸ”‘ MID-LEVEL QUESTIONS

Q25How does MERGE work internally? (3 steps)

  1. Join: Inner join target with source on the match condition
  2. Classify: Each row is classified as "matched" or "not matched"
  3. Write: Rewrite affected data files with updates + append new files for inserts

Q26Why is MERGE slow on large tables? How to optimize?

MERGE must scan the ENTIRE target to find matches. Optimizations:

  • Liquid Clustering on merge key β†’ scans only relevant files (biggest win)
  • Filter source early β†’ MERGE INTO target USING (SELECT * FROM source WHERE date = today)
  • OPTIMIZE before MERGE β†’ fewer small files to scan
  • Low shuffle merge β†’ reduces data movement between nodes
  • Partition by merge key β†’ prunes irrelevant partitions (old approach)

⚠️ Q27What is the merge_key trick for SCD Type 2?

Problem: In SCD Type 2, when a record changes, you need to:

  1. Close the old row (set end_date, is_current = false)
  2. Insert a new row (with new values, is_current = true)
But standard MERGE can't INSERT and UPDATE for the SAME key! Trick: Create a merge_key column:
  • For rows to UPDATE: merge_key = booking_id (matches target)
  • For rows to INSERT: merge_key = NULL (never matches β†’ goes to NOT MATCHED β†’ INSERT)

⚑ MUST KNOW DIRECT QUESTIONS

Q28What is OPTIMIZE?

Compacts many small Parquet files into fewer large files (target: ~1 GB each). Faster reads because fewer files to open.

Q29What is VACUUM?

Physically deletes old Parquet files that are no longer referenced by the transaction log. Frees up storage.

Q30What is the default VACUUM retention?

7 days (168 hours). Files older than this are deleted. ⚠️ Setting it lower breaks time travel!

⚠️ Q31Can you VACUUM with 0 hours retention?

Yes, but DANGEROUS β€” breaks time travel and concurrent reads. Only use for GDPR "right to be forgotten":

sql
SET spark.databricks.delta.retentionDurationCheck.enabled = false;
VACUUM bookings RETAIN 0 HOURS;

Q32What is Z-ORDER?

Sorts data within files by specific columns so that similar values are stored together. When you filter by those columns, Delta reads far fewer files.

Q33What is Liquid Clustering?

The new replacement for partitioning + Z-ORDER (Databricks 2024+). Automatically maintains data layout β€” no manual OPTIMIZE ZORDER needed. You just define clustering keys at table creation.

⚠️ Q34Can you use Liquid Clustering with partitioning?

NO! They are mutually exclusive. Liquid Clustering replaces partitioning. You must choose one or the other.

Q35What are Deletion Vectors?

Instead of rewriting an entire Parquet file to delete a few rows, Delta marks rows as "deleted" in a small side file. The data file stays untouched. Much faster deletes/updates.

Q36Are Deletion Vectors enabled by default?

Yes, on Databricks (since 2024). They're a table property: delta.enableDeletionVectors = true.

πŸ”‘ MID-LEVEL QUESTIONS

Q37OPTIMIZE vs VACUUM β€” what's the difference?

OPTIMIZEVACUUM
WhatMerges small files β†’ big filesDeletes old unused files
GoalFaster readsSave storage space
Data loss?NeverOld versions become unreadable
When to runAfter many small writesAfter OPTIMIZE (clean up old files)
Best practiceRun dailyRun weekly

Q38Z-ORDER vs Liquid Clustering β€” when to use which?

Z-ORDERLiquid Clustering
EraOld (before 2024)New (2024+, recommended)
SetupRun manually: OPTIMIZE ... ZORDER BYDefine once: CLUSTER BY at table creation
MaintenanceMust re-run after every writeAuto-maintains incrementally
With partitioning?Yes, works with partitionsNO β€” replaces partitioning
Column changesMust rewrite entire tableALTER TABLE ... CLUSTER BY (new_cols) β€” easy
Use whenLegacy tables, can't migrateAll new tables (always prefer this)

Q39When should you NOT use partitioning?

  • When partition column has HIGH cardinality (>10,000 values) β†’ too many tiny folders
  • When table is small (<1 TB) β†’ partitioning adds overhead
  • When using Liquid Clustering β†’ they're mutually exclusive
  • Rule of thumb: Each partition should be >1 GB. If not, don't partition.

⚠️ Q40What's wrong with too many small files? (The Small File Problem)

  • Each file needs a separate read operation β†’ high I/O overhead
  • More files = more metadata in the transaction log β†’ slower planning
  • Cloud storage (ADLS) charges per API call β†’ more files = higher cost
  • Fix: OPTIMIZE (compaction) or Auto Loader with trigger-based batching

⚑ MUST KNOW DIRECT QUESTIONS

Q41What is time travel in Delta Lake?

Ability to query previous versions of a table using version numbers or timestamps. Works because old Parquet files are kept until VACUUM removes them.

Q42How to query a specific version?

SELECT * FROM bookings VERSION AS OF 5; or TIMESTAMP AS OF '2026-03-20';

Q43How to see all versions of a table?

DESCRIBE HISTORY bookings; β€” shows every version, timestamp, operation, and user.

Q44How to recover from accidental deletion?

RESTORE TABLE bookings TO VERSION AS OF 5; β€” rolls back to version 5. This creates a NEW version (safe, doesn't rewrite history).

⚠️ Q45When does time travel stop working?

After VACUUM runs β€” it physically deletes old files. Default retention is 7 days. If you VACUUM with 0 hours, ALL old versions are gone immediately.

SECTION 5: LAKEHOUSE ARCHITECTURE

🧠 Memory Map: Lakehouse

DATA LAKE DATA WAREHOUSE LAKEHOUSE
───────── ────────────── ─────────
Cheap storage Expensive storage Cheap storage βœ“
Any format Structured only Any format βœ“
No ACID Full ACID Full ACID βœ“
Schema-on-read Schema-on-write Both βœ“
Slow queries Fast queries Fast queries (Photon) βœ“
No governance Full governance Full governance (Unity Catalog) βœ“
Good for ML Bad for ML Good for ML βœ“
Bad for BI Good for BI Good for BI βœ“
Remember: "Lakehouse = Lake PRICE + Warehouse FEATURES"

⚑ MUST KNOW DIRECT QUESTIONS

Q46What is a Data Lakehouse?

A single platform combining the cheap storage of a data lake with the ACID transactions, schema enforcement, and governance of a data warehouse. No need for separate lake + warehouse.

Q47What makes Lakehouse possible? (4 technologies)

  1. Delta Lake β†’ ACID transactions on data lake files
  2. Photon Engine β†’ Fast SQL queries (warehouse-speed on lake data)
  3. Unity Catalog β†’ Governance, security, lineage
  4. Serverless SQL Warehouses β†’ On-demand compute, no cluster management

Q48Managed Table vs External Table?

ManagedExternal
Data locationDatabricks controlsYou control (your ADLS path)
DROP TABLEDeletes data + metadataDeletes metadata ONLY, data stays
Use whenMost cases (simpler)Data shared across platforms

SECTION 6: NEW 2025-2026 FEATURES

🧠 Memory Map: What's New

NEW FEATURES"PLMCV"
PPredictive Optimization (auto OPTIMIZE + VACUUM)
LLakebase (OLTP on Delta Lake β€” like PostgreSQL)
MMulti-table Transactions (BEGIN ATOMIC...END)
CCompatibility Mode (Iceberg clients read Delta tables)
VVariant type (store JSON natively in Delta columns)
Remember: "PLM-CV" = Product Lifecycle Management for your CV (career!)

⚑ MUST KNOW DIRECT QUESTIONS

Q49What is Predictive Optimization?

Databricks automatically runs OPTIMIZE and VACUUM based on table usage patterns. You don't schedule these manually anymore. Unity Catalog required.

Q50What is Lakebase?

A new feature (GA March 2026) that allows Delta tables to handle OLTP workloads (point lookups, single-row updates) β€” like a traditional database (PostgreSQL) but built on Delta Lake.

Q51What are Multi-table Transactions?
βœ… Pro Tip
BEGIN ATOMIC ... END β€” allows changes to multiple tables in a single atomic transaction. Either ALL tables update or NONE do. New in Databricks 2025.
Q52What is Compatibility Mode?

Allows external tools (that only speak Iceberg/Hive) to read your Delta tables without conversion. Unity Catalog rewrites metadata on-the-fly.

Q53What is the Variant type?

A new data type in Delta Lake 4.x that stores semi-structured JSON data natively β€” no need to stringify JSON. Faster queries on nested JSON fields.

Delta Lake Advanced Masterclass -- Never Get Caught Off Guard Again

πŸ’‘ Interview Tip
Prep Time: 8-10 hours Purpose: Covers every advanced Delta Lake topic interviewers ask about -- especially the ones that trip up experienced candidates Covers: OPTIMIZE deep dive, Liquid Clustering vs Z-ORDER vs Partitioning, UniForm, Change Data Feed, Deletion Vectors, Predictive Optimization, Small File Problem, VACUUM Gotchas, Delta vs Iceberg vs Hudi

TABLE OF CONTENTS

  1. OPTIMIZE -- When It Helps and When It Hurts
  2. Liquid Clustering vs Z-ORDER vs Partitioning -- The Complete Decision Guide
  3. Deletion Vectors -- Internal Mechanics
  4. Change Data Feed (CDF) -- CDC with Delta Lake
  5. UniForm -- Universal Format
  6. Predictive Optimization
  7. The Small File Problem -- Root Causes and Real Solutions
  8. VACUUM -- Risks, Production Incidents, and Gotchas
  9. Delta Lake vs Apache Iceberg vs Apache Hudi
  10. Rapid-Fire Interview Questions with Traps

Answer First: Choose Iceberg for a multi-engine or Snowflake-heavy ecosystem, Delta for a Databricks-centered platform with deep native integration, and Hudi when record-level upserts and incremental ingestion dominate.

Memory Map: When would you choose each format? Give me the decision framework -> engine ecosystem establishes interoperability constraints -> mutation workload selects transaction semantics -> platform services determine operational fit -> cross-engine pilot verifies the format choice [DB_06_Delta_Lake_Advanced_Masterclass.md:1228].

Q23: When would you choose each format? Give me the decision framework.

Answer:

πŸ—‚οΈChoose DELTA LAKE when:
You are using Databricks (it is the native format -- first-class support)
Your team is Spark-centric and uses PySpark/SparkSQL primarily
You need tight integration with Unity Catalog, MLflow, etc.
You are on Azure (Databricks is dominant on Azure)
You want features like Liquid Clustering, Predictive Optimization
You value the Databricks ecosystem and support
Choose APACHE ICEBERG when:
You need multi-engine support (Trino, Flink, Dremio, Snowflake, etc.)
You are NOT locked into Databricks
You need hidden partitioning (partition evolution without rewriting data)
You are on AWS (Iceberg has strong AWS ecosystem: Athena, EMR, Glue)
You want engine-agnostic architecture (hedge against vendor lock-in)
Snowflake/BigQuery integration is important (both support Iceberg natively)
Choose APACHE HUDI when:
You have heavy CDC/streaming upsert workloads (Hudi was built for this)
You need record-level indexing (Hudi's unique record-level index)
Your primary workload is: small, frequent upserts into large tables
You need Merge-on-Read as a first-class storage model (not bolted on)
Note: Hudi adoption has slowed relative to Delta and Iceberg

The nuanced interview answer: "In practice, the choice is often driven by ecosystem, not features. If you are on Databricks, you use Delta Lake. If you are multi-engine or Snowflake-heavy, you lean toward Iceberg. Hudi is the best choice for streaming upsert-heavy workloads but has a smaller community. The formats are converging -- Delta has UniForm for Iceberg compatibility, Iceberg added merge-on-read, and all three support ACID, time travel, and schema evolution."

Answer First: delta.enableChangeDataFeed is a table property that records change data for future commits; readChangeFeed is a reader option that requests those recorded rows. Enabling capture does not make earlier versions retroactively available.

Memory Map: the difference between "delta.enableChangeDataFeed" and "readChangeFeed" -> table property enables recording for future commits -> read option requests the recorded change stream -> enabling later cannot reconstruct earlier mutations -> history and sample reads verify the boundary [DB_06_Delta_Lake_Advanced_Masterclass.md:1313].

Q25: What is the difference between "delta.enableChangeDataFeed" and "readChangeFeed"?

Answer:

πŸ—‚οΈdelta.enableChangeDataFeed = TABLE PROPERTY
Set at table level (ALTER TABLE SET TBLPROPERTIES)
Controls whether the table RECORDS changes
Must be enabled BEFORE the changes you want to capture happen
Storage cost: creates extra files in _change_data/
readChangeFeed = READ OPTION
Set at query time (spark.read.option("readChangeFeed", "true"))
Controls whether your QUERY reads the change data
Only works if the table has enableChangeDataFeed = true
No additional storage cost

Answer First: Ordered log actions, checkpoint-assisted reconstruction, conflict checks, and atomic publication provide the transaction behavior asked about here.

Memory Map: the Delta Lake protocol -- reader and writer versions -> protocol action declares minimum reader and writer capabilities -> table features can raise those versions -> incompatible clients reject access safely -> detail output confirms negotiated requirements [DB_06_Delta_Lake_Advanced_Masterclass.md:1412].

Q29: Explain the Delta Lake protocol -- reader and writer versions.

Answer:

Delta Lake uses protocol versioning to manage backward/forward compatibility.
DESCRIBE DETAIL my_table;
-- Shows: minReaderVersion, minWriterVersion
Protocol versions and what they enable:
Reader v1: Basic Delta reading (anyone can read)
Reader v2: Column mapping (columns can be renamed/dropped without rewrite)
Reader v3: Deletion vectors, timestamp without timezone type
Writer v2: Append-only tables, column invariants
Writer v3: CHECK constraints, generated columns
Writer v4: Change Data Feed
Writer v5: Column mapping
Writer v6: Identity columns
Writer v7: Deletion vectors, table features, row tracking,
domain metadata, timestamp without timezone
Protocol upgrades are compatibility boundaries, but they are not universally irreversible.
On **Databricks Runtime 16.3 and above**, `ALTER TABLE ... DROP FEATURE`
can remove supported features and downgrade the protocol to the lowest versions
required by the remaining features. The operation can rewrite data and metadata,
conflicts with concurrent writes, and **not all table features can be dropped**.
Legacy clients that do not understand the remaining features or
`checkpointProtection` can still fail. A full legacy-client downgrade can require
`DROP FEATURE ... TRUNCATE HISTORY`, a documented two-step process that removes
older history and time-travel access. Use it only after compatibility testing:
[official downgrade guide](https://docs.databricks.com/aws/en/tables/features/drop-feature).
This is the #1 production gotcha with new features:
You enable Deletion Vectors on a table
Protocol upgrades to Reader v3, Writer v7
An old Spark 3.2 job that reads this table suddenly fails:
"Delta protocol version 3 is too new for this client"
Fix: Upgrade the Spark version on the old job

Answer First: Write-Audit-Publish isolates a candidate dataset, runs quality and reconciliation checks, then promotes only an approved result. With Delta, use a staging table or clone and an atomic publication step rather than nonexistent table-branch syntax.

Memory Map: Write-Audit-Publish (WAP) pattern with Delta? How do you implement it -> candidate write remains isolated from consumers -> audit queries evaluate rules and counts -> approved data receives an atomic publish step -> rejected data preserves the previous visible version [DB_06_Delta_Lake_Advanced_Masterclass.md:1506].

Q31: What is Write-Audit-Publish (WAP) pattern with Delta? How do you implement it?

Answer:

WAP is a data quality pattern where you:
1. WRITE data to a staging area
2. AUDIT (validate) the data quality
3. PUBLISH only if quality checks pass
Supported implementation with a staging clone:
-- Step 1: WRITE to an isolated candidate table.
CREATE OR REPLACE TABLE bookings_candidate
DEEP CLONE bookings;
MERGE INTO bookings_candidate AS target
USING raw_bookings AS source
ON target.booking_id = source.booking_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *;
-- Step 2: AUDIT the candidate without exposing it to consumers.
SELECT COUNT(*) AS invalid_rows
FROM bookings_candidate
WHERE fare_amount < 0;
-- Step 3: PUBLISH only after validation succeeds.
MERGE INTO bookings AS target
USING bookings_candidate AS source
ON target.booking_id = source.booking_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *;
-- Step 4: Clean up the candidate.
DROP TABLE bookings_candidate;
A shallow clone is cheaper when its source-file lifetime is guaranteed; a deep clone
is independent. A plain staging table plus validation and MERGE is also supported.
See [clone semantics](https://docs.databricks.com/aws/en/tables/operations/clone).

Answer First: Delta row tracking assigns stable row identifiers and records the commit version of row changes for supported workloads. It helps incremental materialization and row-level change correlation, but it is a table feature with protocol compatibility requirements.

Memory Map: Row Tracking and what is it used for -> row tracking and it used for changes row visibility through table metadata -> row-level metadata records identity or removal -> reader combines it with Parquet data -> logical state changes without immediate full rewrite -> later compaction materializes the result [DB_06_Delta_Lake_Advanced_Masterclass.md:1540].

Q32: What is Row Tracking and what is it used for?

Answer:

Row Tracking (Delta Lake 3.x+ / DBR 14.x+) assigns a stable, unique
row ID to every row in a Delta table. The ID persists across rewrites
(OPTIMIZE, UPDATE, etc.).
Enable it:
ALTER TABLE my_table SET TBLPROPERTIES ('delta.enableRowTracking' = true);
Use cases:
1. Precise CDC: Track exactly which rows changed across versions
2. Deletion Vectors: Row tracking enables DVs to reference specific rows
3. Row-level lineage: Know the history of a specific row
4. Deduplication: Identify and merge duplicate rows
IMPORTANT: Enabling row tracking upgrades the protocol version.
This is irreversible and may break older readers.

Answer First: Priority: HIGHEST β€” Delta Lake is 30-40% of any Databricks interview.

Memory Map: Type Widening and Variant types in Delta Lake -> type widening and variant types in delta lake chooses enforcement, widening, or explicit evolution -> incoming fields meet the table contract -> enforcement or explicit evolution decides compatibility -> metadata version records the shape -> downstream reader test validates change [DB_06_Delta_Lake_Advanced_Masterclass.md:1563].

Q33: What are Type Widening and Variant types in Delta Lake?

Answer (latest features):

TYPE WIDENING (Delta Lake 3.2+ / DBR 15.x+)
─────────────────────────────────────────────
Allows certain "safe" type changes without full table rewrite:
INT -> LONG (safe: no precision loss)
FLOAT -> DOUBLE (safe: wider range)
BYTE -> SHORT -> INT (safe: progressive widening)
DATE -> TIMESTAMP (safe: date is subset of timestamp)
ALTER TABLE my_table SET TBLPROPERTIES (
'delta.enableTypeWidening' = true
);
-- Then you can do:
ALTER TABLE my_table ALTER COLUMN price TYPE DOUBLE;
-- (was FLOAT, now DOUBLE -- no rewrite needed!)
Without type widening: changing column types requires full table rewrite.
VARIANT TYPE (Delta Lake 3.3+ / DBR 16.x+)
─────────────────────────────────────────────
A semi-structured data type for JSON-like data:
CREATE TABLE events (
id BIGINT,
payload VARIANT -- stores arbitrary JSON structure
) USING DELTA;
INSERT INTO events VALUES
(1, PARSE_JSON('{"user": "alice", "action": "click", "metadata": {"page": "/home"}}'));
-- Query nested fields:
SELECT payload:user, payload:metadata:page FROM events;
Benefits:
No need to define a fixed schema for semi-structured data
Better performance than storing JSON as STRING (binary format, columnar storage)
Schema-on-read flexibility with Delta's transactional guarantees

Delta Lake β€” Complete Interview Guide

⚠️ Common Trap
Priority: HIGHEST β€” Delta Lake is 30-40% of any Databricks interview Goal: After this page, you should NEVER struggle with a Delta Lake question again Approach: Every topic starts with WHY β†’ WHAT β†’ HOW β†’ Interview trap to avoid

Memory Map

🧠 DELTA LAKE MASTERY β†’ TACOVS
DELTA LAKE MASTERYTACOVS
──────────────────────────────
TTransaction Log (the brain of Delta)
AACID + Architecture (why Delta exists)
CCommands (MERGE, OPTIMIZE, VACUUM, Z-ORDER)
OOptimization (small files, data skipping, Liquid Clustering)
VVersioning (Time Travel, Clones, Recovery)
SSpecial Features (CDF, Deletion Vectors, UniForm, Sharing)

Answer First: Delta Lake is the transaction protocol and storage framework; a Delta table is one dataset made of Parquet files plus its own transaction log. The protocol gives that table atomic commits, snapshot reads, schema controls, and history.

Memory Map: The Simple Answer -> storage framework supplies transaction semantics over files -> one dataset combines Parquet data with its log directory -> readers reconstruct a snapshot from both -> operations publish later versions [Delta_01_Complete_Guide.md:32].

The Simple Answer

Delta Lake = The TECHNOLOGY (the engine, the framework, the system)
Delta Table = ONE TABLE created using that technology
It's like:
MySQL→Delta Lake (the system)
one table→one Delta Table (a table inside that system)

Real-World Analogy

Think of a LIBRARY SYSTEM:
πŸ“š Delta Lake = The entire library management system
The rules (ACID transactions)
The catalogue system (transaction log)
The checkout/return process (reads/writes)
The version history (time travel)
πŸ“– Delta Table = ONE book that the library manages
It follows the library's rules
It's tracked in the catalogue
It has checkout history
It can be restored to any past version
You don't say "I built a MySQL" β€” you say "I built a table IN MySQL"
You don't say "I built a Delta Lake" β€” you say "I built a Delta table USING Delta Lake"

Answer First: Delta Lake is the transaction protocol and storage framework; each Delta table is a concrete dataset made of Parquet data files plus its own _delta_log metadata.

Memory Map: Technical Difference β€” What Each Actually IS -> technology defines the protocol and implementation -> concrete dataset is an instance using that protocol -> metadata binds schema properties and files -> commands operate on the instance [Delta_01_Complete_Guide.md:64].

Technical Difference β€” What Each Actually IS

πŸ“ Architecture Diagram
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      DELTA LAKE (Technology)                 β”‚
β”‚                                                             β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”‚
β”‚  β”‚  Delta Table:    β”‚  β”‚  Delta Table:    β”‚                  β”‚
β”‚  β”‚  orders          β”‚  β”‚  customers       β”‚                  β”‚
β”‚  β”‚                  β”‚  β”‚                  β”‚                  β”‚
β”‚  β”‚  πŸ“ _delta_log/  β”‚  β”‚  πŸ“ _delta_log/  β”‚  ← each table   β”‚
β”‚  β”‚  πŸ“„ data.parquet β”‚  β”‚  πŸ“„ data.parquet β”‚    has its OWN   β”‚
β”‚  β”‚                  β”‚  β”‚                  β”‚    transaction   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    log            β”‚
β”‚                                                             β”‚
β”‚  Provides: ACID, Schema Enforcement, Time Travel,           β”‚
β”‚            MERGE, OPTIMIZE, VACUUM, Data Skipping           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Side-by-Side Comparison

AspectDelta LakeDelta Table
What is it?Open-source storage layer/frameworkA single table stored in Delta format
Created byDatabricks (open-sourced in 2019)You, using CREATE TABLE ... USING DELTA
How many?ONE per environment (it's the technology)MANY β€” you create hundreds of Delta tables
ContainsThe protocol, the rules, the engineParquet data files + _delta_log/ folder
AnalogyThe operating system (Windows)One file on that operating system
Lives where?Runs inside Spark/Databricks runtimeStored on S3 / ADLS / GCS / DBFS
VersionDelta Lake 3.x (the protocol version)Table has its own version history (0, 1, 2, 3...)

Answer First: ) USING DELTA -- ← tells Spark: "use Delta Lake format".

Memory Map: How You Create a Delta Table (using Delta Lake) -> USING DELTA selects the table provider -> schema and location establish metadata -> first commit records protocol and creation state -> catalog or path resolves the new dataset [Delta_01_Complete_Guide.md:96].

How You Create a Delta Table (using Delta Lake)

python β€” editable
# Method 1: SQL
spark.sql("""
    CREATE TABLE orders (
        order_id INT,
        customer STRING,
        amount DOUBLE,
        order_date DATE
    ) USING DELTA                          -- ← tells Spark: "use Delta Lake format"
    LOCATION 's3://my-bucket/orders/'
""")

# Method 2: DataFrame write
df.write.format("delta").save("s3://my-bucket/orders/")
#                 ↑
#          this "delta" = use Delta Lake technology

# Method 3: In Databricks (default since DBR 8.0+)
spark.sql("CREATE TABLE orders (...)")
#   ↑ In Databricks, DELTA is the DEFAULT format
#     You don't even need to say USING DELTA!

# What gets created on disk:
# s3://my-bucket/orders/
# β”œβ”€β”€ _delta_log/                    ← Delta Lake adds this (transaction log)
# β”‚   └── 00000000000000000000.json  ← First commit: "table created with schema..."
# β”œβ”€β”€ part-00000.snappy.parquet      ← Actual data (still Parquet!)
# └── part-00001.snappy.parquet      ← More data

Answer First: Without Delta Lake (plain Parquet): With Delta Lake (Delta Table):.

Memory Map: What Delta Lake GIVES to a Delta Table -> ordered commits add atomic mutation and recovery -> schema checks protect writes -> statistics support file pruning -> maintenance improves physical layout [Delta_01_Complete_Guide.md:149].

What Delta Lake GIVES to a Delta Table

Without Delta Lake (plain Parquet): With Delta Lake (Delta Table):
❌ No transactions βœ… ACID transactions
❌ Overwrite = data gone forever βœ… Time Travel (undo any change)
❌ Two writers = corrupt data βœ… Optimistic concurrency control
❌ Schema? What schema? βœ… Schema enforcement + evolution
❌ UPDATE one row = rewrite entire file βœ… MERGE handles insert/update/delete
❌ 10,000 small files = slow queries βœ… OPTIMIZE compacts files
❌ Scan everything for every query βœ… Data skipping (min/max stats)
❌ Delete data = rewrite files manually βœ… DELETE command + VACUUM cleanup

Answer First: "Delta Lake is an open-source storage layer that sits on top of cloud storage like S3 or ADLS. It adds ACID transactions, schema enforcement, time travel, and scalable metadata handling to data lakes.

Memory Map: Interview Answers β€” What to Say -> concise response names open files plus transaction metadata -> mechanism explains atomic versioned commits -> tradeoff notes protocol-aware readers -> example connects the model to production [Delta_01_Complete_Guide.md:163].

Interview Answers β€” What to Say

Q"What is Delta Lake?"

"Delta Lake is an open-source storage layer that sits on top of cloud storage like S3 or ADLS. It adds ACID transactions, schema enforcement, time travel, and scalable metadata handling to data lakes. Under the hood, data is still stored as Parquet files β€” Delta Lake adds a transaction log that tracks every change."

Q"What is a Delta Table?"

"A Delta table is a specific table stored in Delta format. It's a collection of Parquet data files plus a _delta_log folder that contains the transaction history. Every Delta table has its own independent transaction log."

Q"What's the difference between Delta Lake and Delta Table?"

"Delta Lake is the technology β€” the protocol and engine. A Delta table is one table that uses that technology. It's like MySQL vs a table in MySQL. Delta Lake provides the ACID guarantees, and each Delta table is an individual dataset that benefits from those guarantees."

What NOT to Say:

🧠 Memory Map
❌ "Delta Lake and Delta Table are the same thing"
β†’ No. Lake = technology, Table = one table using that technology
❌ "Delta Lake is a database"
β†’ No. It's a STORAGE LAYER. It doesn't have a query engine.
Spark/Databricks is the engine. Delta Lake is the format.
❌ "Delta Table is different from Parquet"
β†’ Partially wrong. Delta Table IS Parquet + transaction log.
The data files are still .parquet format.
❌ "Delta Lake stores data in its own format"
β†’ No. Data is standard Parquet. Only the _delta_log is Delta-specific.

Answer First: Delta LAKE = the L is for LAYER (it's a storage LAYER / technology).

Memory Map: Quick Memory Trick -> layer mnemonic distinguishes technology from dataset -> log-plus-files mnemonic recalls table structure -> version mnemonic recalls atomic commits -> example reinforces the distinction [Delta_01_Complete_Guide.md:191].

Quick Memory Trick

Delta LAKE = the L is for LAYER (it's a storage LAYER / technology)
Delta TABLE = the T is for TABLE (it's one specific TABLE)
LAKELarge system, like a real lake (holds everything)
TABLESmall unit, like a table inside a restaurant (one specific thing)

Answer First: Delta Lake adds a transaction log ( delta log/ ) on top of Parquet files. This log tracks every change β€” which files were added, removed, or modified. It turns a chaotic collection of files into a proper table with ACID transactions, schema enforcement, and version history.

Memory Map: Delta Lake? Why do we need it -> plain files lack coordinated mutation state -> ordered log actions define each snapshot -> optimistic checks serialize conflicting writers -> history enables audit and recovery [Delta_01_Complete_Guide.md:205].

Q01 β€” What is Delta Lake? Why do we need it?

Question: Your interviewer asks: "Why can't we just use Parquet files on a data lake? Why do we need Delta Lake?"

The Problem (Plain Parquet):

πŸ—‚οΈdata_lake/
orders_2026_01.parquet ← File 1
orders_2026_02.parquet ← File 2
orders_2026_03.parquet ← File 3
PROBLEM 1: Two pipelines write to orders_2026_03.parquet at the same time
β†’ Data gets CORRUPTED. No transaction support.
PROBLEM 2: You accidentally overwrite orders_2026_02.parquet
β†’ Gone forever. No undo, no history, no time travel.
PROBLEM 3: A pipeline writes half the data and crashes
β†’ Half-written file stays. Readers see partial/garbage data.
PROBLEM 4: Source system adds a new column "discount_pct"
β†’ Old files don't have it. Schema mismatch everywhere.
PROBLEM 5: You want to UPDATE one row in a 2 GB Parquet file
β†’ You must rewrite the ENTIRE 2 GB file. Parquet is immutable.

The Solution (Delta Lake): Delta Lake adds a transaction log (_delta_log/) on top of Parquet files. This log tracks every change β€” which files were added, removed, or modified. It turns a chaotic collection of files into a proper table with ACID transactions, schema enforcement, and version history.

πŸ—‚οΈdelta_table/
_delta_log/ ← THE BRAIN β€” tracks every change
00000000000000000000.json ← Commit 0: table created
00000000000000000001.json ← Commit 1: 1000 rows inserted
00000000000000000002.json ← Commit 2: 50 rows updated
00000000000000000010.checkpoint.parquet ← Checkpoint (summary)
_last_checkpoint ← Points to latest checkpoint
part-00000-abc123.snappy.parquet ← Actual data file 1
part-00001-def456.snappy.parquet ← Actual data file 2
part-00002-ghi789.snappy.parquet ← Actual data file 3

One-liner for interviews: "Delta Lake is an open-source storage layer that adds ACID transactions, scalable metadata handling, and time travel to existing data lakes built on Parquet."

Answer First: Delta Lake combines atomic commits, snapshot isolation, schema controls, versioned history, scalable metadata, and maintenance over Parquet data. Together these features turn a file collection into a reliable transactional table.

Memory Map: the key features of Delta Lake? (The Top 8) -> atomic commits protect concurrent changes -> enforcement and evolution govern schema -> history and change feeds expose versions -> statistics layout and cleanup sustain performance [Delta_01_Complete_Guide.md:252].

Q02 β€” What are the key features of Delta Lake? (The Top 8)

Question: "List the main features of Delta Lake and explain why each matters."

#FeatureWhat It DoesWhy It Matters
1ACID TransactionsEvery write is atomic β€” all or nothingNo more corrupted tables from failed writes
2Time TravelQuery any historical version of the tableUndo mistakes, audit changes, debug issues
3Schema EnforcementRejects writes that don't match the table schemaPrevents bad data from entering your table
4Schema EvolutionAutomatically adds new columns when neededHandles evolving source systems gracefully
5MERGE (Upsert)INSERT + UPDATE + DELETE in one atomic operationThe #1 operation for data engineering pipelines
6Data SkippingSkips files that can't contain query resultsMakes queries 10-100x faster on large tables
7Unified Batch + StreamingSame table for both batch and streaming writesNo separate streaming tables needed
8Open FormatData stored as Parquet (open standard)No vendor lock-in β€” read with Spark, Trino, Presto, etc.

Interview Tip: Don't just list features β€” connect them to PROBLEMS they solve. "We needed MERGE because our booking system sends daily change feeds that contain inserts, updates, and cancellations β€” all in one file."

What NOT to Say: "Delta Lake is a database." β€” No, it's a STORAGE LAYER on top of cloud storage. The data still lives as Parquet files on ADLS/S3/GCS.

Q28 β€” Managed vs External Tables

Question: "What is the difference? When do you use each?"

AspectManaged TableExternal Table
Data locationDatabricks-managed (auto)YOUR storage path (you specify)
DROP TABLEDeletes metadata AND dataDeletes metadata ONLY β€” data survives
Predictive OptimizationYes (automatic)No
Best forMost tables (default)Shared data, legacy migration
sql
-- Managed (recommended for new tables)
CREATE TABLE catalog.schema.orders (order_id LONG, amount DECIMAL);
-- Data stored in Databricks-managed location
-- DROP TABLE deletes everything

-- External (for data shared with other systems)
CREATE TABLE catalog.schema.legacy_orders (order_id LONG, amount DECIMAL)
LOCATION 'abfss://container@storage.dfs.core.windows.net/legacy/orders/';
-- Data stays at YOUR path even if table is dropped

Q32 β€” What's new in Delta Lake 4.x?

Question: "What are the latest features? Mention 2-3 to show you're up to date."

FeatureVersionWhat It Does
Variant Data Type4.0Store semi-structured JSON without schema β€” query nested fields directly
Type Widening4.0Change column type (INT β†’ BIGINT) without rewriting data
Coordinated Commits4.0Multiple engines can safely write to the same Delta table
UniForm4.0Auto-generate Iceberg/Hudi metadata for cross-engine reads
Conflict-Free DV4.1Enable Deletion Vectors without blocking concurrent writers
Atomic CTAS4.1CREATE TABLE AS SELECT is fully atomic (no partial tables on failure)
Lakebase2026Serverless PostgreSQL-compatible database inside Databricks

Interview power move: "I'd use Liquid Clustering with Deletion Vectors enabled and CDF turned on β€” that gives me automatic compaction, fast updates, and incremental ETL. For cross-team sharing with Snowflake users, I'd enable UniForm so they read the same table as Iceberg."

Answer First: Run the lab as a controlled state transition: create a disposable baseline, perform one operation, inspect table history and files, then verify the expected version and rows. Reset only the isolated lab location between attempts.

Memory Map: (This section replaces YouTube videos β€” read + copy-paste + see the output) -> disposable baseline makes each lab repeatable -> one operation creates one observable transition -> log and table inspection connect cause to effect -> cleanup prevents shared-state contamination [Delta_01_Complete_Guide.md:1474].

(This section replaces YouTube videos β€” read + copy-paste + see the output)

Setup (copy-paste this first)

python β€” editable
# ─── SETUP ────────────────────────────────────────────────
# Create a fresh Delta table in a clean location
path = "/tmp/delta_lab/orders"

# Clean up anything from previous runs
dbutils.fs.rm(path, recurse=True)   # ← wipes old data (safe, /tmp only)

# Create initial data
from pyspark.sql import Row
data = [
    Row(order_id=1, customer="Alice", amount=100.0),
    Row(order_id=2, customer="Bob",   amount=200.0),
    Row(order_id=3, customer="Carol", amount=300.0),
]
df = spark.createDataFrame(data)

# Write as Delta β€” this creates the table AND the _delta_log
df.write.format("delta").save(path)

print("βœ… Delta table created at:", path)

Answer First: A new Delta table contains Parquet data files plus a _delta_log directory whose first JSON commit declares protocol, metadata, and added files. Inspect both locations to connect physical files to the committed snapshot.

Memory Map: Step 1 β€” Look at what Delta just created -> creation writes data files and a transaction directory -> first JSON file receives version zero -> filesystem listing reveals both components -> table query confirms the initial snapshot [Delta_01_Complete_Guide.md:1509].

Step 1 β€” Look at what Delta just created

python β€” editable
# List files in the folder
files = dbutils.fs.ls(path)
for f in files:
    print(f.name)

# EXPECTED OUTPUT:
# _delta_log/                          ← the transaction log folder
# part-00000-abc123.snappy.parquet     ← actual data (Parquet)
# part-00001-def456.snappy.parquet

# Now peek inside _delta_log
log_files = dbutils.fs.ls(f"{path}/_delta_log")
for f in log_files:
    print(f.name)

# EXPECTED OUTPUT:
# 00000000000000000000.json    ← ONE commit file. This is commit #0.

Answer First: The first Delta commit is the table’s creation record: protocol and metadata define compatibility and schema, add actions select the initial files, and commitInfo describes the write. Reading the JSON exposes those actions directly.

Memory Map: Step 2 β€” Read commit #0 (see the "birth certificate" of the table) -> protocol action records compatibility -> metadata action records schema and properties -> add actions identify active files -> version zero becomes the creation certificate [Delta_01_Complete_Guide.md:1531].

Step 2 β€” Read commit #0 (see the "birth certificate" of the table)

python β€” editable
# Read the first commit JSON
commit_0 = spark.read.json(f"{path}/_delta_log/00000000000000000000.json")
commit_0.show(truncate=False)

# EXPECTED OUTPUT (5 rows, one per action):
# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
# β”‚ action   β”‚ details                                 β”‚
# β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
# β”‚ protocol β”‚ minReaderVersion=1, minWriterVersion=2 β”‚  ← compat info
# β”‚ metaData β”‚ schema={order_id INT, customer STR,..} β”‚  ← table schema
# β”‚ add      β”‚ path=part-00000-abc.parquet, size=921  β”‚  ← data file 1
# β”‚ add      β”‚ path=part-00001-def.parquet, size=874  β”‚  ← data file 2
# β”‚ commitInfoβ”‚ operation=WRITE, timestamp=...         β”‚  ← who did what
# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Answer First: A Delta UPDATE publishes a new commit rather than editing the original commit. The new version records the row-level change through file actions while the prior snapshot remains addressable until retention removes required files.

Memory Map: Step 3 β€” Do an UPDATE. Watch a NEW commit appear -> update reads the current snapshot -> changed rows produce new file actions -> atomic commit publishes the next version -> history shows the recorded operation [Delta_01_Complete_Guide.md:1550].

Step 3 β€” Do an UPDATE. Watch a NEW commit appear.

python β€” editable
from delta.tables import DeltaTable
dt = DeltaTable.forPath(spark, path)

# Update Alice's amount from 100 β†’ 150
dt.update(
    condition = "customer = 'Alice'",
    set       = {"amount": "150.0"}
)

# Now check _delta_log AGAIN
log_files = dbutils.fs.ls(f"{path}/_delta_log")
for f in log_files:
    print(f.name)

# EXPECTED OUTPUT:
# 00000000000000000000.json    ← original commit (unchanged)
# 00000000000000000001.json    ← NEW commit! ✨ This is the UPDATE

Answer First: The update commit records removed or logically changed old data plus the new file state and operation metrics. Comparing commit 1 with commit 0 shows how Delta changes snapshots without mutating history.

Memory Map: Step 4 β€” Read commit #1. See what Delta did behind the scenes -> remove action retires replaced file state -> add action introduces rewritten rows -> operation metadata explains the mutation -> snapshot reconstruction uses the combined actions [Delta_01_Complete_Guide.md:1572].

Step 4 β€” Read commit #1. See what Delta did behind the scenes.

python β€” editable
commit_1 = spark.read.json(f"{path}/_delta_log/00000000000000000001.json")
commit_1.show(truncate=False)

# EXPECTED OUTPUT:
# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
# β”‚ action    β”‚ details                                    β”‚
# β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
# β”‚ remove    β”‚ path=part-00000-abc.parquet (OLD file)    β”‚  ← old file marked removed
# β”‚ add       β”‚ path=part-00002-xyz.parquet (NEW file)    β”‚  ← new file with updated row
# β”‚ commitInfoβ”‚ operation=UPDATE, numUpdatedRows=1        β”‚
# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

# 🧠 KEY INSIGHT:
#    Delta NEVER modifies a Parquet file in place.
#    It writes a NEW file with the updated row,
#    and marks the OLD file as "removed" in the log.
#    The old file still EXISTS on disk until VACUUM runs!

Answer First: Delta history returns committed versions with timestamps, operations, and metrics, letting operators trace state changes and choose a recovery point. It describes commits; retained data files still determine whether an old snapshot can be read.

Memory Map: Step 5 β€” Check the history (Delta's "audit log") -> step 5 check history delta s audit log selects the recoverable version and retained-file boundary -> known commit identifies prior state -> retained log and data reconstruct the snapshot -> read, clone, or restore applies recovery -> row reconciliation confirms result [Delta_01_Complete_Guide.md:1594].

Step 5 β€” Check the history (Delta's "audit log")

python β€” editable
dt.history().select("version", "timestamp", "operation", "operationMetrics").show(truncate=False)

# EXPECTED OUTPUT:
# β”Œβ”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
# β”‚versionβ”‚ timestamp         β”‚ operationβ”‚ operationMetrics        β”‚
# β”œβ”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
# β”‚  1    β”‚ 2026-04-05 10:31 β”‚ UPDATE  β”‚ {numUpdatedRows: 1}    β”‚
# β”‚  0    β”‚ 2026-04-05 10:30 β”‚ WRITE   β”‚ {numOutputRows: 3}     β”‚
# β””β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
#
# Every commit = one row in DESCRIBE HISTORY. That's your audit trail.

Routed Delta question-bank section indexes

TOPIC 1: DELTA LAKE (Internals, Transaction Log, ACID, MERGE, OPTIMIZE, VACUUM, Z-ORDER, Liquid Clustering)

L1 β€” Direct / Simple Questions

L2

Advanced

Delta Transaction Log and ACID

#

Delta Transaction Log and ACID

Answer First: A Delta reader reconstructs a table snapshot from ordered log actions and checkpoints; writers use optimistic concurrency and atomic commit publication.

Memory Map: actions -> commit version -> checkpoint -> snapshot -> conflict check -> publish.

SECTION 1: DELTA LAKE INTERNALS

Answer First: The Delta transaction log records ordered metadata plus add and remove file actions. Readers reconstruct a snapshot from those actions, while atomic commit versions and optimistic validation provide ACID concurrency.

Memory Map: the Delta Lake transaction log (_delta_log)? Explain how it ensures ACID transactions -> candidate metadata and file actions enter validation -> atomic publication creates one ordered version -> readers reconstruct a stable snapshot -> history and active files prove isolation [03_Delta_Lake_and_Lakehouse.md:26].

Q1: What is the Delta Lake transaction log (_delta_log)? Explain how it ensures ACID transactions.

Simple Explanation: Think of a bank ledger. Every deposit, withdrawal, and transfer is recorded in order. If someone asks "what was the balance yesterday?", you replay the ledger up to yesterday. The Delta transaction log works the same way β€” it records every change to your table in numbered JSON files.

Answer: The _delta_log/ directory is an ordered record of every transaction performed on a Delta table.

Structure:

πŸ—‚οΈmy_table/
_delta_log/
00000000000000000000.json ← Commit 0
00000000000000000001.json ← Commit 1
...
00000000000000000010.checkpoint.parquet ← Checkpoint at commit 10
_last_checkpoint ← Points to latest checkpoint
part-00000-abc123.snappy.parquet
part-00001-def456.snappy.parquet
...

Each JSON commit file contains:

  • add actions: New Parquet files added
  • remove actions: Files logically deleted (still physically present until VACUUM)
  • metaData: Schema changes, table properties
  • protocol: Reader/writer version
  • commitInfo: Timestamp, operation, user, metrics

How ACID is ensured:

  • Atomicity: Each commit writes a single JSON file atomically using put-if-absent (filesystem rename)
  • Consistency: Schema enforcement rejects mismatched writes
  • Isolation: Snapshot isolation β€” readers see a consistent snapshot
  • Durability: Data persisted as Parquet files on durable cloud storage

Interview Tip: "They won't ask 'What is ACID?' β€” they'll ask 'How does Delta Lake ensure data consistency when multiple pipelines write to the same table?' Answer with the transaction log + optimistic concurrency."

What NOT to Say: "Delta Lake is a database" β€” No, it's a STORAGE LAYER on top of Parquet files on cloud storage.

Answer First: A checkpoint materializes the table state at a version so readers need only replay later JSON commits. This bounds snapshot reconstruction time as transaction history grows.

Memory Map: long commit history -> checkpoint captures active metadata and files -> later JSON actions advance the snapshot -> bounded replay time proves the benefit [03_Delta_Lake_and_Lakehouse.md:69].

Q2: What are checkpoint files? Why are they critical?

Simple Explanation: Imagine your bank account has 10,000 transactions since it was opened. To know your current balance, you'd need to add up ALL 10,000 transactions β€” very slow! A monthly statement gives you the current balance directly. Checkpoints are like monthly statements for Delta β€” a snapshot of the full table state so you don't replay every commit.

Answer: Every 10 commits (default), Delta creates a Parquet checkpoint file that consolidates the full table state at that point.

Why needed:

  • Without checkpoints: Reading version 10,000 requires replaying 10,000 JSON files
  • With checkpoints: Read the latest checkpoint + only subsequent JSON files
  • _last_checkpoint file stores the latest checkpoint version

Configurable: delta.checkpointInterval (default: 10)

Interview Tip: "If asked 'what happens when a Delta table has millions of commits?', mention checkpoints immediately β€” it shows you understand the internals, not just the API."

Answer First: Two hotel receptionists are booking rooms at the same time. "Optimistic" means: we ASSUME they won't book the same room, let both work, and only check for conflicts when they try to save. If they booked different rooms β†’ both succeed.

Memory Map: optimistic concurrency control in Delta Lake. What happens when two writers conflict -> writer records its read version -> proposed actions identify touched data -> commit-time comparison detects conflict -> retry or new version resolves contention [03_Delta_Lake_and_Lakehouse.md:89].

Q3: Explain optimistic concurrency control in Delta Lake. What happens when two writers conflict?

Simple Explanation: Two hotel receptionists are booking rooms at the same time. "Optimistic" means: we ASSUME they won't book the same room, let both work, and only check for conflicts when they try to save. If they booked different rooms β†’ both succeed. If they booked the same room β†’ one gets an error and retries.

Answer:

πŸ—‚οΈWriter A reads version 5
Writer B reads version 5
Writer A commits version 6 (succeeds β€” first to commit)
Writer B attempts to commit version 6:
Checks: version 6 already exists!
Reads version 6 to see what Writer A changed
Checks for LOGICAL conflict:
If Writer A and B touched DIFFERENT files β†’ No conflict β†’ Retries as version 7 βœ“
If Writer A and B touched SAME files/predicates β†’ CONFLICT β†’ ConcurrentModificationException βœ—
Retry is automatic (default: 3 times, configurable)

Isolation levels:

LevelDefault?Behavior
WriteSerializableYesWriters see consistent snapshots; non-conflicting concurrent writes succeed
SerializableNoStrictest; even reads during writes can cause conflicts

Interview Tip: "To avoid conflicts in practice: partition your writes so different pipelines touch different files, and add the partition column to your MERGE condition."

What NOT to Say: "Delta locks the whole table during writes" β€” No, Delta uses optimistic concurrency, NOT pessimistic locking.

Answer First: Data files (Parquet) may be partially written to storage.

Memory Map: What happens if a write fails midway in Delta Lake -> writer may leave unreferenced data files in storage -> failed validation never publishes a new version -> readers continue using the previous snapshot -> cleanup later removes orphaned files [03_Delta_Lake_and_Lakehouse.md:207].

Q7: What happens if a write fails midway in Delta Lake?

Answer:

  • Data files (Parquet) may be partially written to storage
  • But the transaction log entry is never committed (atomic operation)
  • On next read, Delta only considers files referenced in committed log entries
  • The orphaned Parquet files are cleaned up by VACUUM
  • This is the key benefit of ACID β€” partial writes don't corrupt the table

Interview Tip: "The key insight: the commit JSON file is the SINGLE point of atomicity. Data files can be partially written β€” it doesn't matter until the commit JSON makes them visible."

SECTION 1: DELTA LAKE INTERNALS (1.5 hours)

Answer First: But there's a big problem: if two people write to the same folder at the same time, data can get corrupted.

Memory Map: Delta Lake? And what is the transaction log -> Parquet files hold columnar rows -> ordered JSON actions define active file state -> atomic version creation coordinates writers -> checkpoints accelerate snapshot reconstruction [DB_01_Delta_Lake_Deep_Dive.md:11].

Q1: What is Delta Lake? And what is the transaction log?

Simple Explanation: Think of a normal data lake β€” you store files (like Parquet) in cloud storage (Azure ADLS). But there's a big problem: if two people write to the same folder at the same time, data can get corrupted. There's no "undo" button. There's no way to know what changed.

Delta Lake solves this. It adds a "smart layer" on top of your Parquet files. This smart layer is called the transaction log (stored in a folder called _delta_log/). It's like a diary that records every change β€” "file X was added", "file Y was removed", "schema changed", etc.

Real-world analogy: Imagine a hotel booking register. Every time a booking is made or cancelled, the receptionist writes it in a numbered logbook (commit 1, commit 2, commit 3...). If someone asks "what did our bookings look like yesterday?", you can replay the logbook up to yesterday. That logbook = Delta transaction log.

Why do we need it?

  • Without Delta: Two booking agents update same file β†’ data gets corrupted
  • With Delta: Transaction log ensures only one change goes through at a time (like a queue)

Technical details:

πŸ—‚οΈbookings_table/ -- Your table folder on ADLS Gen2
_delta_log/ -- THE TRANSACTION LOG (the "diary")
00000000000000000000.json -- Commit 0: table was created
00000000000000000001.json -- Commit 1: 1000 bookings inserted
00000000000000000002.json -- Commit 2: 50 bookings updated
00000000000000000010.checkpoint.parquet -- Checkpoint (summary of first 10 commits)
_last_checkpoint -- Points to the latest checkpoint file
part-00000-abc123.snappy.parquet -- Actual data file 1
part-00001-def456.snappy.parquet -- Actual data file 2

What's inside each JSON commit file?

  • add β†’ "I added this new Parquet file" (new data was written)
  • remove β†’ "I logically deleted this file" (but file is still physically there until VACUUM cleans it)
  • metaData β†’ "The table schema changed" or "table properties changed"
  • commitInfo β†’ "Who did this, when, what operation (INSERT/UPDATE/DELETE)"

What is ACID? (You know this from databases, same concept here):

PropertyWhat It MeansHow Delta Does It
AtomicityEither ALL changes apply, or NONE apply. No half-done writes.Each commit is a single JSON file β€” it either fully writes or doesn't
ConsistencyData always follows the rules (schema). You can't insert wrong data types.Schema enforcement rejects mismatched columns/types
IsolationReaders don't see half-written data. Each reader sees a clean snapshot.Snapshot isolation β€” when you start a query, you see the table as it was at that moment
DurabilityOnce data is committed, it won't be lost (even if server crashes).Data is stored as Parquet files on ADLS Gen2 (cloud storage = durable)

Example: "When 10 booking agents update the same passenger table simultaneously, Delta's transaction log ensures no partial writes corrupt the table. Each agent's changes are atomic β€” either fully applied or not at all."

Interview tip: They won't ask "What is ACID?". They'll ask "How does Delta Lake ensure data consistency when multiple pipelines write to the same table?" β€” answer with the transaction log + optimistic concurrency.

Answer First: To read the current table state, you'd need to read ALL 10,000 files β€” very slow!.

Memory Map: checkpoint files? Why are they important -> transaction log accumulates JSON commit actions -> checkpoint compacts log state into Parquet -> readers combine it with newer commits -> snapshot reconstruction time verifies the benefit [DB_01_Delta_Lake_Deep_Dive.md:59].

Q2: What are checkpoint files? Why are they important?

Simple Explanation: Imagine your transaction log has 10,000 commits (10,000 JSON files). To read the current table state, you'd need to read ALL 10,000 files β€” very slow!

A checkpoint is a summary file. Every 10 commits (by default), Delta creates a single Parquet file that says "here's the complete state of the table right now." So instead of reading 10,000 files, you read 1 checkpoint + the few commits after it.

Real-world analogy: Like a bank account statement. Instead of adding up every transaction since the account was opened, the monthly statement gives you the current balance. You only need to add transactions after the statement date.

Key points:

  • Created every 10 commits (configurable via delta.checkpointInterval)
  • Format: Parquet (not JSON) β€” faster to read
  • _last_checkpoint file β†’ tells Delta which checkpoint is the latest
  • Without checkpoints: reading table = replaying ALL commits (slow!)
  • With checkpoints: reading table = read latest checkpoint + only a few recent JSONs (fast!)

Q3: What is Optimistic Concurrency Control?

Simple Explanation: When two people try to update the same table at the same time, Delta uses "optimistic concurrency" to handle it. The word "optimistic" means: Delta ASSUMES there won't be a conflict, lets both work, and only checks for conflicts at commit time.

Real-world analogy: Two travel agents are updating different passenger records in the same table. Agent A updates passenger 1's email. Agent B updates passenger 2's address. Both started with version 5 of the table. Agent A finishes first and creates version 6. When Agent B tries to commit, Delta checks: "Did Agent A touch the same data as Agent B?" If no β†’ Agent B's changes go in as version 7. If yes β†’ conflict error.

πŸ—‚οΈWriter A reads version 5 (fare update batch)
Writer B reads version 5 (booking cancellation batch)
Writer A commits version 6 β†’ succeeds (it was first)
Writer B tries to commit version 6:
Sees: "Wait, version 6 already exists!"
Reads version 6 to see what Writer A changed
Checks for LOGICAL conflict:
Different files/rows touched β†’ No conflict β†’ Auto-retries as version 7 βœ“
Same files touched β†’ CONFLICT β†’ throws ConcurrentModificationException βœ—
Automatic retry happens up to 3 times (configurable)

Two isolation levels:

LevelDefault?When to Use
WriteSerializableYesNormal workloads β€” two pipelines writing to different parts of the table can run in parallel
SerializableNoStrict audit/compliance tables β€” even reads during writes can cause conflicts

Interview tip: If asked "How do you handle concurrent writes?", mention: optimistic concurrency + partition your writes by date/region so different pipelines touch different files β†’ no conflicts.

SECTION 1: DELTA LAKE INTERNALS

🧠 Memory Map: Transaction Log

πŸ—‚οΈ_delta_log/ = "The BRAIN of Delta Lake"
_delta_log/ = "The BRAIN of Delta Lake"
JSON files = individual commits (diary entries)
Checkpoint = summary every 10 commits (monthly bank statement)
_last_checkpoint = pointer to latest summary
Remember: "JC-L" = JSON β†’ Checkpoint β†’ Last_checkpoint

SECTION 0: DELTA LAKE vs DELTA TABLE β€” The #1 Confusion

πŸ’‘ Interview Tip
Why this section exists: Most people mix up "Delta Lake" and "Delta Table." Interviewers LOVE testing this. Clear this confusion FIRST, everything else becomes easy.

Answer First: The Key Insight β€” Delta Table = Parquet + Transaction Log resolves to ordered log actions, checkpoint-assisted snapshot reconstruction, conflict checks, and a published commit version.

Memory Map: The Key Insight β€” Delta Table = Parquet + Transaction Log -> query pattern defines projection needs -> columnar encoding and codec trade CPU for size -> reader prunes and decodes data -> bytes scanned validate the format choice [Delta_01_Complete_Guide.md:128].

The Key Insight β€” Delta Table = Parquet + Transaction Log

πŸ—‚οΈRegular Parquet file:
πŸ“„ data.parquet ← Just a file. No history. No ACID. No schema check.
Delta Table:
πŸ“ my_table/
_delta_log/ ← THIS is what makes it "Delta"
00000.json ← Commit 0: created table
00001.json ← Commit 1: inserted 1000 rows
00002.json ← Commit 2: updated 50 rows
00003.json ← Commit 3: deleted 10 rows
part-00000.snappy.parquet ← Same Parquet format as before!
part-00001.snappy.parquet
part-00002.snappy.parquet
So Delta Table = Parquet files + _delta_log folder
Delta Lake = The technology that READS and WRITES that _delta_log

SECTION 1: WHY DELTA LAKE EXISTS

Q03 β€” How does Delta Lake ensure ACID transactions?

Question: "Walk me through how each ACID property is implemented in Delta Lake."

PropertyWhat It MeansHow Delta Does It
AtomicityAll changes in a transaction succeed, or none doEach commit writes ONE JSON file atomically using put-if-absent (cloud storage rename). If it fails, nothing changes.
ConsistencyData always follows schema rulesSchema enforcement rejects writes with wrong column types/names
IsolationConcurrent readers/writers don't interfereSnapshot isolation β€” readers see the table as it was when they started reading. Writers use optimistic concurrency (see Q05).
DurabilityCommitted data survives crashesData is stored as Parquet on cloud storage (S3/ADLS/GCS) β€” inherently durable

Follow-up they'll ask: "What happens if a write fails midway?"

🧠 Memory Map
Step 1: Spark writes new Parquet data files to storage ← files exist but NOT committed
Step 2: Spark tries to write the commit JSON to _delta_log ← THIS is the atomic step
If Step 2 FAILS:
β†’ The Parquet files from Step 1 are "orphaned" (exist but not referenced)
β†’ No commit JSON means Delta doesn't know about them
β†’ Next VACUUM will clean them up
β†’ Table state is unchanged β€” ZERO corruption
If Step 2 SUCCEEDS:
β†’ Transaction is committed
β†’ Readers will see the new data

Interview Tip: "The key insight is that the commit JSON file is the SINGLE point of atomicity. Data files can be partially written β€” it doesn't matter until the commit JSON makes them visible."

SECTION 2: TRANSACTION LOG DEEP DIVE

Answer First: Each JSON commit file contains these actions.

Memory Map: the Delta Transaction Log (_delta_log)? What's inside each commit -> commit file contains protocol metadata add remove and operation actions -> monotonically increasing filename orders versions -> readers apply actions to prior state -> history exposes the resulting transaction [Delta_01_Complete_Guide.md:309].

Q04 β€” What is the Delta Transaction Log (_delta_log)? What's inside each commit?

Question: "Explain the Delta transaction log in detail. What exactly is stored in each commit file?"

Each JSON commit file contains these actions:

json
// Example: 00000000000000000005.json (Commit #5)
{
  "commitInfo": {
    "timestamp": 1711670400000,
    "operation": "MERGE",
    "operationParameters": {"predicate": "t.id = s.id"},
    "operationMetrics": {
      "numTargetRowsInserted": "1500",
      "numTargetRowsUpdated": "300",
      "numTargetRowsDeleted": "50"
    }
  },
  "add": {
    "path": "part-00000-new-file.snappy.parquet",
    "size": 1073741824,
    "partitionValues": {"date": "2026-03-29"},
    "stats": "{\"numRecords\":50000,\"minValues\":{\"id\":1},\"maxValues\":{\"id\":50000}}"
  },
  "remove": {
    "path": "part-00000-old-file.snappy.parquet",
    "deletionTimestamp": 1711670400000,
    "dataChange": true
  }
}
ActionWhat It RecordsExample
addNew Parquet file was added to the tableAfter INSERT, MERGE, or OPTIMIZE
removeFile was logically deleted (still physically exists!)After UPDATE, DELETE, or OPTIMIZE
metaDataSchema change, table properties changedAfter ALTER TABLE or schema evolution
protocolReader/writer version requirementsAfter enabling new features (e.g., Deletion Vectors)
commitInfoWho, when, what operation, and metricsEvery commit has this

Critical detail: remove is LOGICAL, not physical

🧠 Memory Map
remove: "part-00000-old-file.snappy.parquet"
β†’ File is marked as "no longer part of current table state"
β†’ File STILL EXISTS on disk (for time travel!)
β†’ File is PHYSICALLY deleted only when VACUUM runs
β†’ Default: VACUUM deletes files older than 7 days

Interview Tip: "The transaction log is append-only β€” you never modify existing commit files, you only add new ones. This is what makes it safe for concurrent readers."

Answer First: Large checkpoint state can be written across multiple checkpoint parts that readers load together. The parts represent one checkpoint version, not independent commits.

Memory Map: large table state -> one checkpoint version is split into numbered parts -> reader loads every part before later commits -> complete part set reconstructs the snapshot [Delta_01_Complete_Guide.md:364].

Q05 β€” What are checkpoint files? Why are they critical?

Question: "Without checkpoints, what happens when a Delta table has 100,000 commits?"

Problem without checkpoints:

🧠 Memory Map
To read the current table state at version 100,000:
β†’ Read 00000000000000000000.json (commit 0)
β†’ Read 00000000000000000001.json (commit 1)
β†’ Read 00000000000000000002.json (commit 2)
β†’ ... replay ALL 100,000 JSON files
β†’ VERY SLOW β€” just to know which Parquet files to read!

Solution β€” Checkpoints:

🧠 Memory Map
Every 10 commits (default), Delta creates a checkpoint file:
β†’ 00000000000000000010.checkpoint.parquet ← State at commit 10
β†’ 00000000000000000020.checkpoint.parquet ← State at commit 20
β†’ ...
β†’ 00000000000000099990.checkpoint.parquet ← State at commit 99,990
To read at version 100,000:
β†’ Read checkpoint at 99,990 (ONE Parquet file β€” fast!)
β†’ Replay only 10 JSON files (99,991 to 100,000)
β†’ Done! Instead of 100,000 files, we read ~11 files

Key details:

  • Checkpoint interval: delta.checkpointInterval (default: 10)
  • _last_checkpoint file points to the latest checkpoint
  • Checkpoints are in Parquet format (columnar, fast to read)
  • Multi-part checkpoints exist for very large tables (splits into multiple files)

Q06 β€” How does Optimistic Concurrency Control work?

Question: "Two pipelines write to the same Delta table at the same time. What happens?"

πŸ—‚οΈPipeline A reads table at version 5 (fare update batch)
Pipeline B reads table at version 5 (booking cancellation batch)
Pipeline A finishes first:
β†’ Writes commit 6 to _delta_log β†’ SUCCEEDS (first to commit)
Pipeline B tries to commit:
β†’ Attempts to write commit 6 β†’ FAILS (file already exists!)
β†’ Delta automatically:
1. Reads commit 6 to see what Pipeline A changed
2. Checks for LOGICAL conflict:
Pipeline A touched files X, Y
Pipeline B touched files M, N
X,Y ∩ M,N = empty β†’ NO CONFLICT
Pipeline B retries as commit 7 β†’ SUCCEEDS βœ“
β†’ But if both touched the SAME files:
CONFLICT β†’ throws ConcurrentModificationException βœ—
Automatic retry (up to 3 times by default, configurable)

Two isolation levels:

LevelDefault?BehaviorUse When
WriteSerializableYesConcurrent writes to different files succeedNormal ETL workloads
SerializableNoEven reads during writes can conflictStrict audit/compliance tables

How to AVOID conflicts in practice:

  1. Partition writes by date/region so different pipelines touch different files
  2. Add partition column to MERGE condition: ON t.id = s.id AND t.date = s.date
  3. Use Liquid Clustering to minimize file overlap

Interview Tip: "The word 'optimistic' is key β€” Delta assumes no conflict, lets both writers proceed, and only checks at commit time. This is efficient because most concurrent writes DON'T conflict."

VISUAL ANIMATION 1 β€” Optimistic Concurrency Conflict

What happens when TWO jobs try to update the same table at the same time?

πŸ“ Architecture Diagram
TIME  β†’  T0              T1               T2               T3
         β”‚               β”‚                β”‚                β”‚
JOB A    β”œβ”€ reads v5 ────┼─ writes data ──┼─ commits v6 β”€βœ… β”‚
         β”‚               β”‚                β”‚                β”‚
JOB B    β”œβ”€ reads v5 ────┼─ writes data ──┼────────────────┼─ tries to commit v6 ❌
         β”‚               β”‚                β”‚                β”‚    CONFLICT!
         β”‚               β”‚                β”‚                β”‚    Job A already wrote v6
         β”‚               β”‚                β”‚                β”‚
         β”‚               β”‚                β”‚                β”œβ”€ Delta checks:
         β”‚               β”‚                β”‚                β”‚    "Did Job A touch the
         β”‚               β”‚                β”‚                β”‚     same files as me?"
         β”‚               β”‚                β”‚                β”‚
         β”‚               β”‚                β”‚                β”œβ”€ IF NO overlap:
         β”‚               β”‚                β”‚                β”‚    retry commit as v7 βœ…
         β”‚               β”‚                β”‚                β”‚
         β”‚               β”‚                β”‚                └─ IF YES overlap:
         β”‚               β”‚                β”‚                     throw ConcurrentModification ❌
         β”‚               β”‚                β”‚                     β†’ user must retry the WHOLE job

KEY INSIGHT:
  β€’ Delta does NOT lock the table while writing (unlike traditional DBs)
  β€’ Both jobs write data files in PARALLEL β€” no slowdown
  β€’ Conflict is detected ONLY at commit time (when writing the JSON)
  β€’ Only ONE commit can "win" version 6 β†’ the other must retry or fail

Routed Delta question-bank prompts β€” 10-transaction-log-acid

2. What file format does Delta Lake use under the hood?

Apache Parquet files plus a JSON-based transaction log (_delta_log). Data is stored as Parquet; the log tracks which Parquet files are valid for each table version.

3. What is the _delta_log directory and what does it contain?

A directory inside every Delta table that stores the transaction log β€” a sequence of JSON files (one per commit) recording every add/remove of Parquet files. It is the single source of truth for the table's state.

5. What is a checkpoint file in the Delta transaction log?

A Parquet file created every 10 commits that snapshots the entire table state. It avoids reading all previous JSON commits from scratch β€” readers start from the latest checkpoint and replay only newer commits.

  1. Explain the anatomy of a Delta Lake transaction β€” what happens when you write to a Delta table?

    Key points: (1) Spark writes new Parquet files to storage, (2) Creates a new JSON commit file in _delta_log listing added/removed files, (3) Uses optimistic concurrency β€” checks if any conflicting commits happened since the read, (4) If no conflict, commit succeeds atomically; if conflict, retries or fails. Mention that readers never see partial writes because they only read committed versions.

17. What is the difference between managed and external Delta tables? > Key points: Managed: Databricks manages both metadata AND data files. Dropping the table deletes the data. External: Databricks manages only metadata; data files live at a user-specified location. Dropping the table leaves the data intact. Use managed for most tables (simpler lifecycle). Use external when data is shared across platforms or you need control over the storage location.

20. Explain Delta Lake 4.x features: UniForm, Universal Format. Why do they matter? > Key points: UniForm generates Iceberg-compatible metadata alongside Delta metadata, allowing Iceberg readers (Snowflake, BigQuery, Trino) to read Delta tables natively. No data duplication β€” same Parquet files, dual metadata. Matters because it breaks the "table format war" β€” you can use Delta for writes and let external tools read via Iceberg. Reduces vendor lock-in and enables multi-engine architectures.

4. What are the four ACID properties and how does Delta Lake guarantee them?

Atomicity (commits are all-or-nothing via the transaction log), Consistency (schema enforcement rejects bad writes), Isolation (optimistic concurrency control with conflict detection), Durability (data stored on cloud storage like ADLS Gen2).

  1. What is Delta Lake and why was it created?

    Open-source storage layer that brings ACID transactions, schema enforcement, and time travel to data lakes. Created to solve the reliability problems of raw data lakes (no transactions, no schema control, corrupt reads from concurrent writes).

14. What is the difference between Delta Lake and Apache Parquet? > Parquet is a file format. Delta Lake is a storage layer built ON TOP of Parquet that adds ACID transactions, schema enforcement, time travel, and the transaction log. Parquet alone has no transactions or versioning.

2. How does optimistic concurrency control work in Delta Lake? What happens during write conflicts?

Key points: Writers don't lock the table β€” they write Parquet files, then try to commit. At commit time, Delta checks if the files you read have been modified by another concurrent writer. If files overlap β†’ ConcurrentModificationException and retry. If files don't overlap (e.g., different partitions) β†’ both commits succeed. Mention disjoint writes succeed, overlapping writes conflict.

7. How does the Delta transaction log handle concurrent writes from multiple clusters?

Key points: Each cluster writes independently. At commit time, it tries to create the next numbered JSON file (e.g., 000010.json). Cloud storage provides atomic file creation β€” only one writer wins. The other detects the conflict, reads the new commit, checks for logical conflicts (overlapping files), and either retries or fails. Disjoint partition writes always succeed concurrently.

Advanced

Delta MERGE and Schema Evolution

#

Delta MERGE and Schema Evolution

Answer First: Safe MERGE pipelines deduplicate the source, constrain the match domain, make clause ordering explicit, and treat schema evolution as a governed contract change.

Memory Map: deduplicate -> match -> update/insert/delete -> validate schema -> audit result.

SECTION 2: MERGE INTO β€” ALL SCENARIOS

Answer First: MERGE joins a source relation to a target and applies ordered matched, not-matched, and not-matched-by-source clauses in one atomic table commit.

Memory Map: the MERGE INTO syntax. Write a basic upsert -> deduplicated source joins the bounded target -> matched clause applies the update -> unmatched clause inserts a new key -> one commit publishes both actions [03_Delta_Lake_and_Lakehouse.md:223].

Q8: Explain the MERGE INTO syntax. Write a basic upsert.

Simple Explanation: MERGE is the Swiss Army knife of Delta Lake β€” it combines INSERT + UPDATE + DELETE into ONE atomic command. Think of it like: "Look at my new data. Compare it with existing data. If a record exists β†’ update it. If it's new β†’ insert it. If it's cancelled β†’ delete it." All in one go, safely.

Answer:

sql
MERGE INTO target_table AS t
USING source_table AS s
ON t.id = s.id                         -- Match condition

WHEN MATCHED AND s.op = 'DELETE' THEN
    DELETE                              -- Delete matching rows

WHEN MATCHED AND s.updated_at > t.updated_at THEN
    UPDATE SET *                        -- Update with all source columns

WHEN NOT MATCHED THEN
    INSERT *                            -- Insert all source columns

WHEN NOT MATCHED BY SOURCE THEN
    DELETE                              -- Delete target rows not in source (Databricks extension)

PySpark equivalent:

python β€” editable
from delta.tables import DeltaTable

target = DeltaTable.forName(spark, "target_table")

target.alias("t").merge(
    source_df.alias("s"),
    "t.id = s.id"
).whenMatchedUpdate(
    condition="s.updated_at > t.updated_at",
    set={"*": "*"}  # or explicit: {"name": "s.name", "email": "s.email"}
).whenNotMatchedInsertAll() \
 .execute()

Interview Tip: "Be ready to write MERGE from memory β€” this is the #1 coding question in Databricks interviews. Practice it 3-4 times until it flows naturally."

What NOT to Say: "I'll write separate INSERT, UPDATE, and DELETE statements" β€” That's not atomic. MERGE does all three in one transaction.

Answer First: Multiple source rows for one target key make an update or delete ambiguous. Rank source rows by a deterministic sequence and retain one row per business key before MERGE.

Memory Map: you handle duplicate keys in the source during MERGE -> deterministic ordering ranks rows within each business key -> deduplication retains one source winner -> uniqueness assertion runs before ordered clauses -> unambiguous match can commit safely [03_Delta_Lake_and_Lakehouse.md:271].

Q9: How do you handle duplicate keys in the source during MERGE?

Simple Explanation: MERGE needs each target row to match at most ONE source row. If your source has two records for the same ID, MERGE doesn't know which one to use β†’ error. Fix: deduplicate the source first using ROW_NUMBER.

Answer: If source has duplicate keys matching the same target row, MERGE throws an error: "Cannot perform Merge as multiple source rows matched...".

Solution: Deduplicate source first:

python β€” editable
from pyspark.sql.functions import row_number, col
from pyspark.sql import Window

w = Window.partitionBy("id").orderBy(col("updated_at").desc())
deduped_source = source_df \
    .withColumn("rn", row_number().over(w)) \
    .filter(col("rn") == 1) \
    .drop("rn")

# Now merge with deduped source
target.alias("t").merge(deduped_source.alias("s"), "t.id = s.id") \
    .whenMatchedUpdateAll() \
    .whenNotMatchedInsertAll() \
    .execute()

Answer First: Model order events by business key, deduplicate the staged source, then order matched clauses so cancellation and update rules cannot shadow each other.

Memory Map: a MERGE for an e-commerce order system (create, update, cancel) -> staging retains the newest event per order -> matched cancellation precedes matched update -> unmatched records insert new orders -> operation counts reconcile every event type [03_Delta_Lake_and_Lakehouse.md:299].

Q10: Design a MERGE for an e-commerce order system (create, update, cancel).

Answer:

sql
MERGE INTO orders_fact t
USING orders_staging s
ON t.order_id = s.order_id

-- Cancel: mark as deleted
WHEN MATCHED AND s.status = 'CANCELLED' THEN DELETE

-- Update: only if newer
WHEN MATCHED AND s.updated_at > t.updated_at THEN
    UPDATE SET
        t.status = s.status,
        t.amount = s.amount,
        t.shipping_address = s.shipping_address,
        t.updated_at = s.updated_at

-- New order
WHEN NOT MATCHED THEN
    INSERT (order_id, customer_id, status, amount, shipping_address, created_at, updated_at)
    VALUES (s.order_id, s.customer_id, s.status, s.amount, s.shipping_address, s.created_at, s.updated_at)

Answer First: MERGE performance depends on how much target data the match predicate can prune and how many files its actions must rewrite. Bound the target domain, reduce and deduplicate the source, then inspect scan and rewrite metrics.

Memory Map: the performance concern with MERGE? How do you optimize it -> selective match predicate prunes target files -> source reduction lowers join work -> favorable layout reduces touched data -> scan and rewrite metrics identify the bottleneck [03_Delta_Lake_and_Lakehouse.md:326].

Q11: What is the performance concern with MERGE? How do you optimize it?

Answer: MERGE scans the entire target table to find matches. For large tables, this is the bottleneck.

Optimization techniques:

1. Partition pruning β€” add partition columns to ON clause:

sql
MERGE INTO target t
USING source s
ON t.id = s.id AND t.date = s.date  -- date is partition column β†’ prunes partitions

2. Z-ORDER on merge key:

sql
OPTIMIZE target ZORDER BY (id);  -- Data skipping on the merge key

3. Reduce source data before merge:

python β€” editable
# Don't merge 100M rows if only 1M changed
changed_records = source_df.join(target_df, "id", "left_anti")  # New records
changed_records = changed_records.union(
    source_df.join(target_df, "id", "inner")
    .filter(source_df["hash"] != target_df["hash"])  # Changed records
)

4. Broadcast small source:

python β€” editable
target.alias("t").merge(
    broadcast(source_df).alias("s"),  # Broadcast if source is small
    "t.id = s.id"
)

5. Compact target first:

sql
OPTIMIZE target;  -- Fewer, larger files = fewer tasks

6. Use Photon runtime β€” significantly faster MERGE operations.

Answer First: MERGE schema evolution can add source fields when assignments directly map compatible columns and evolution is enabled. Explicit assignments keep the accepted schema change visible and testable.

Memory Map: MERGE with schema evolution β€” how does it work -> direct compatible assignments expose new source fields -> explicit evolution updates table metadata -> incompatible changes still fail validation -> schema history confirms the committed shape [03_Delta_Lake_and_Lakehouse.md:372].

Q12: MERGE with schema evolution β€” how does it work?

Answer:

python β€” editable
# Enable automatic schema merge
spark.conf.set("spark.databricks.delta.schema.autoMerge.enabled", "true")

# Now source with new columns will automatically add them to target
target.alias("t").merge(
    source_df.alias("s"),  # source has columns not in target
    "t.id = s.id"
).whenMatchedUpdateAll() \
 .whenNotMatchedInsertAll() \
 .execute()
# New columns from source are added to target table

Answer First: Schema enforcement rejects incompatible writes before they change the table, while schema evolution accepts only explicitly permitted changes and updates table metadata. Treat evolution as a governed contract because downstream readers must understand the new schema.

Memory Map: schema evolution in Delta Lake. What are the options -> enforcement rejects incompatible writes before commit -> mergeSchema allows explicit additive changes -> session auto-merge broadens evolution scope -> downstream compatibility tests gate adoption [03_Delta_Lake_and_Lakehouse.md:641].

Q21: Explain schema evolution in Delta Lake. What are the options?

Answer:

Add new columns during write:

python β€” editable
# mergeSchema β€” adds new columns, preserves existing
df.write.format("delta") \
    .mode("append") \
    .option("mergeSchema", "true") \
    .saveAsTable("my_table")

Replace entire schema:

python β€” editable
# overwriteSchema β€” replaces schema completely
df.write.format("delta") \
    .mode("overwrite") \
    .option("overwriteSchema", "true") \
    .saveAsTable("my_table")

Column rename/drop (requires column mapping):

sql
-- Enable column mapping first
ALTER TABLE my_table SET TBLPROPERTIES ('delta.columnMapping.mode' = 'name');

-- Now you can rename and drop columns
ALTER TABLE my_table RENAME COLUMN old_name TO new_name;
ALTER TABLE my_table DROP COLUMN unused_column;

SECTION 2: MERGE INTO β€” ALL SCENARIOS (1.5 hours)

Q5: What is MERGE? Basic syntax (upsert)

Simple Explanation: MERGE is the most important operation in Databricks. It combines INSERT + UPDATE + DELETE into a single command. In simple words: "Look at my new data (source). Compare it with existing data (target). If a record already exists β†’ update it. If it's new β†’ insert it. If it's cancelled β†’ delete it."

This is called an upsert (update + insert).

Why do we need it? Without MERGE, you'd need to write 3 separate queries (one for insert, one for update, one for delete) β€” and they wouldn't be atomic. MERGE does everything in one atomic operation.

Real-world analogy: The platform receives a daily file of booking changes. Some are new bookings (INSERT), some are updates to existing bookings (UPDATE), some are cancellations (DELETE). MERGE handles all three in one go.

sql
-- MERGE = Compare source (new data) with target (existing table), then act
MERGE INTO bookings_fact AS t            -- t = target (our existing bookings table)
USING bookings_staging AS s              -- s = source (new booking data that just arrived)
ON t.booking_id = s.booking_id           -- Match condition: how to find matching records

-- Case 1: Booking exists AND is cancelled β†’ delete it
WHEN MATCHED AND s.status = 'CANCELLED' THEN
    DELETE

-- Case 2: Booking exists AND has been updated β†’ update the record
WHEN MATCHED AND s.updated_at > t.updated_at THEN
    UPDATE SET
        t.status = s.status,             -- Update status (e.g., CONFIRMED β†’ CHECKED_IN)
        t.fare_amount = s.fare_amount,   -- Maybe fare was recalculated
        t.passenger_count = s.passenger_count,
        t.updated_at = s.updated_at      -- Track when this change happened

-- Case 3: Booking doesn't exist in target β†’ it's a brand new booking β†’ insert
WHEN NOT MATCHED THEN
    INSERT (booking_id, flight_id, passenger_id, status, fare_amount,
            passenger_count, created_at, updated_at)
    VALUES (s.booking_id, s.flight_id, s.passenger_id, s.status, s.fare_amount,
            s.passenger_count, s.created_at, s.updated_at)

-- Case 4: Record exists in target but NOT in source β†’ orphaned data β†’ clean up
-- (Databricks extension β€” not available in standard SQL)
WHEN NOT MATCHED BY SOURCE AND t.status = 'PENDING' THEN
    DELETE

Interview tip: Be ready to write MERGE from memory. This is the #1 coding question in Databricks interviews. Practice it 3-4 times.

Q7: How to make MERGE faster? (CRITICAL β€” commonly asked)

Simple Explanation: The biggest problem with MERGE is: it has to scan the ENTIRE target table to find matching records. If your bookings table has 2 billion rows, MERGE reads all 2 billion rows just to match a few thousand new records. This is VERY slow.

Why is it slow? MERGE works like this: for every row in the source, scan the entire target to find a match. More target data = slower MERGE.

6 ways to make it faster:

1. Partition pruning β€” tell MERGE which partition to look in:

sql
-- WITHOUT partition column: MERGE scans ALL data across ALL dates
MERGE INTO bookings t USING staging s
ON t.booking_id = s.booking_id              -- Scans 2 billion rows!

-- WITH partition column: MERGE only scans matching date partitions
MERGE INTO bookings t USING staging s
ON t.booking_id = s.booking_id
   AND t.booking_date = s.booking_date      -- Only scans today's partition!
-- This is like telling MERGE: "only look in March 2026 folder, not all folders"

2. Z-ORDER on merge key β€” organize data so matching records are close together:

sql
-- Z-ORDER sorts/groups data by booking_id within files
-- This makes data skipping work better during MERGE
OPTIMIZE bookings ZORDER BY (booking_id);
-- Now when MERGE looks for booking ABC123, it can skip most files
-- (See Q10 below for full explanation of Z-ORDER)

3. Don't merge rows that haven't changed β€” filter source first:

sql
-- Problem: Source has 1 million rows, but only 10,000 actually changed
-- Without filter: MERGE processes all 1 million rows (wasteful)
-- With filter: MERGE only processes 10,000 changed rows (fast!)

MERGE INTO bookings t
USING (
    SELECT s.* FROM staging s
    LEFT JOIN bookings t ON s.booking_id = t.booking_id
    WHERE t.booking_id IS NULL                  -- Brand new bookings (not in target)
       OR s.hash_value != t.hash_value          -- Changed bookings (different data)
    -- Unchanged bookings are filtered out β€” saves time!
) AS s
ON t.booking_id = s.booking_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *

4. Broadcast small source β€” if source is small, send it to all workers:

python β€” editable
# What is broadcast? When source is small (e.g., 10K rows),
# Spark sends a copy to every worker machine.
# This avoids expensive "shuffle" (moving data between machines).
from pyspark.sql.functions import broadcast
target.alias("t").merge(
    broadcast(source_df).alias("s"),       # Send source to all workers
    "t.booking_id = s.booking_id"          # Workers can match locally
)
# Use when: source < 100 MB. Don't use when source is large.

5. Compact target first β€” fewer files = fewer tasks = faster:

sql
-- If target has 50,000 small files, MERGE creates 50,000 tasks (slow!)
-- OPTIMIZE combines small files into ~1 GB files (e.g., 500 files)
OPTIMIZE bookings;
-- Now MERGE only creates ~500 tasks (much faster!)
-- See Q9 below for full explanation of OPTIMIZE

6. Use Photon runtime β€” C++ engine that's 3-5x faster for MERGE:

  • Just select "Photon" runtime when creating your cluster
  • No code changes needed β€” same SQL, just runs faster
  • See Day 3 for full Photon explanation

Q8: What is schema evolution with MERGE?

Simple Explanation: Sometimes your source data has NEW columns that don't exist in the target table yet. For example, the airline adds a "loyalty_tier" field to passenger data. Normally, MERGE would fail because the target table doesn't have this column.

With schema evolution, Delta automatically adds the new column to the target table during MERGE.

sql
-- Step 1: Enable auto schema merge (tell Delta: "it's okay if source has new columns")
SET spark.databricks.delta.schema.autoMerge.enabled = true;

-- Step 2: MERGE as normal β€” new columns from source get added to target automatically
MERGE INTO passengers t                -- Target: has columns (id, name, email)
USING staging s                        -- Source: has columns (id, name, email, loyalty_tier) ← NEW!
ON t.passenger_id = s.passenger_id
WHEN MATCHED THEN UPDATE SET *         -- * means "all columns" β€” includes loyalty_tier
WHEN NOT MATCHED THEN INSERT *         -- New column "loyalty_tier" is auto-added to target table
-- After this: target table now has (id, name, email, loyalty_tier)

When to use: When source systems add new fields over time (very common in real life). When NOT to use: When you want strict schema control (e.g., regulatory tables where schema changes need approval).

SECTION 2: MERGE INTO (UPSERT)

🧠 Memory Map: MERGE

🧠 MERGE = "Match β†’ Update, No Match β†’ Insert"
MERGE"Match β†’ Update, No Match β†’ Insert"
MERGE INTO target USING source -- Compare target with source
ON target.id = source.id -- Match condition
WHEN MATCHED THEN UPDATE SET ... -- Found?β†’Update
WHEN NOT MATCHED THEN INSERT ... -- Not found?β†’Insert
Remember: "MU-NI" = Matched→Update, NotMatched→Insert
6 MERGE Optimizations = "FSCPZL"
FFilter early (WHERE clause in source subquery)
SSmall file compaction (OPTIMIZE before MERGE)
CCluster by merge key (Liquid Clustering)
PPartition pruning (match on partition column)
ZZ-ORDER on merge key (if not using Liquid Clustering)
LLow shuffle merge (set spark.databricks.delta.merge.lowShuffle.enabled = true)

Answer First: DVs make MERGE faster at write time (no file rewrites)

Memory Map: Deletion Vectors interact with MERGE -> row-position metadata marks changed records -> write avoids immediately rewriting whole files -> readers merge markers with Parquet rows -> later compaction materializes clean file state [DB_06_Delta_Lake_Advanced_Masterclass.md:434].

Q9: How do Deletion Vectors interact with MERGE?

Answer -- this is a nuanced interview question:

sql
MERGE INTO target USING source
ON target.id = source.id
WHEN MATCHED THEN UPDATE SET target.value = source.value
WHEN NOT MATCHED THEN INSERT *

Without DVs (traditional):

For each matched row:
1. Find the Parquet file containing the row
2. Rewrite the ENTIRE file with the updated row
Result: Even updating 1 row in a 1 GB file = 1 GB rewrite

With DVs enabled:

For each matched row:
1. Find the Parquet file containing the row
2. Mark the OLD row as deleted in a deletion vector (soft delete)
3. Write the NEW (updated) row to a NEW small file
Result: No file rewrite! Just a DV bitmap + a small new file
But wait -- this creates small files:
1000 updates = 1000 small files (one per update batch, roughly)
After MERGE, you should run OPTIMIZE to compact these small files

The trade-off interviewers want to hear:

  • DVs make MERGE faster at write time (no file rewrites)
  • But they create more files and require OPTIMIZE sooner
  • Net effect: MERGE throughput improves 2-10x with DVs enabled
  • This is especially impactful for tables with frequent small MERGEs

Answer First: A deterministic source, explicit match domain, ordered change clauses, and result validation are the required controls for this MERGE or schema case.

Memory Map: What happens if a MERGE operation fails midway -> candidate file actions remain unpublished during execution -> failure prevents atomic version creation -> retry starts from a fresh snapshot -> history confirms no partial mutation [DB_06_Delta_Lake_Advanced_Masterclass.md:1351].

Q27: What happens if a MERGE operation fails midway?

Answer:

Nothing bad happens. This is the ACID guarantee.
MERGE workflow:
1. Read source and target data
2. Compute matched/unmatched rows
3. Write new data files (INSERT/UPDATE results)
4. Try to commit: write a new JSON file to _delta_log/
If failure happens at step 1-3:
New data files are written to storage but NOT referenced in the log
These are "orphaned" files
VACUUM will clean them up later
Table state is unchanged -- as if MERGE never ran
If failure happens at step 4:
The commit JSON file either fully writes (atomic file create) or doesn't
If it doesn't write: same as above -- orphaned files, VACUUM cleans up
If it writes: MERGE succeeded, even if the client didn't get confirmation
Key insight: The commit (step 4) is the atomicity boundary.
Everything before commit is "tentative." The table only changes when
the commit JSON is successfully written.

Q30: How does Delta Lake handle schema enforcement vs schema evolution?

Answer:

SCHEMA ENFORCEMENT (default behavior -- protects your table)
Any write that doesn't match the table schema is REJECTED
"Your DataFrame has column 'price' as STRING but table expects DOUBLE"
Prevents data quality issues at the source
df.write.format("delta").mode("append").saveAsTable("bookings")
# If df has extra column or wrong type -> AnalysisException!
SCHEMA EVOLUTION (opt-in -- when you WANT schema changes)
# Option 1: Merge schema (add new columns, keep existing)
df.write.format("delta") \
.mode("append") \
.option("mergeSchema", "true") \
.saveAsTable("bookings")
# New columns in df are ADDED to the table schema
# Existing data gets NULL for the new columns
# Option 2: Overwrite schema (replace entire schema)
df.write.format("delta") \
.mode("overwrite") \
.option("overwriteSchema", "true") \
.saveAsTable("bookings")
# Table schema is completely replaced with df's schema
# DANGEROUS: existing queries expecting old columns will break
# Option 3: Schema evolution in MERGE
MERGE INTO target USING source ON ...
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *
-- With: spark.databricks.delta.schema.autoMerge.enabled = true
-- New columns from source are automatically added to target

Interview trap: "What is the difference between mergeSchema and overwriteSchema?"

mergeSchema = ADDITIVE. New columns are ADDED. Existing columns unchanged.
overwriteSchema = DESTRUCTIVE. Entire schema is REPLACED.
Example:
Table has: [id, name, email]
DataFrame has: [id, name, phone]
mergeSchema: Table becomes [id, name, email, phone]
(email kept, phone added)
overwriteSchema: Table becomes [id, name, phone]
(email DROPPED, phone added)

SECTION 3: MERGE β€” THE MOST IMPORTANT COMMAND

Q07 β€” Write a MERGE from memory (upsert with all clauses)

Question: "Write a MERGE that handles inserts, updates, and deletes in one command."

This is the #1 coding question in Databricks interviews. Memorize it.

sql
-- MERGE = Compare source (new data) with target (existing table), then act
MERGE INTO orders_fact AS t                -- t = target (existing table)
USING orders_staging AS s                  -- s = source (new data)
ON t.order_id = s.order_id                -- Match condition

-- Case 1: Record exists AND is cancelled β†’ delete it
WHEN MATCHED AND s.status = 'CANCELLED' THEN
    DELETE

-- Case 2: Record exists AND has changed β†’ update it
WHEN MATCHED AND s.updated_at > t.updated_at THEN
    UPDATE SET
        t.status = s.status,
        t.amount = s.amount,
        t.updated_at = s.updated_at

-- Case 3: Record doesn't exist in target β†’ insert it
WHEN NOT MATCHED THEN
    INSERT (order_id, customer_id, status, amount, created_at, updated_at)
    VALUES (s.order_id, s.customer_id, s.status, s.amount, s.created_at, s.updated_at)

-- Case 4: Record in target but NOT in source β†’ clean up (Databricks extension)
WHEN NOT MATCHED BY SOURCE AND t.status = 'PENDING' THEN
    DELETE

PySpark equivalent:

python β€” editable
from delta.tables import DeltaTable

target = DeltaTable.forName(spark, "orders_fact")

target.alias("t").merge(
    source_df.alias("s"),
    "t.order_id = s.order_id"
).whenMatchedDelete(
    condition="s.status = 'CANCELLED'"
).whenMatchedUpdate(
    condition="s.updated_at > t.updated_at",
    set={"status": "s.status", "amount": "s.amount", "updated_at": "s.updated_at"}
).whenNotMatchedInsertAll(
).execute()

Q08 β€” How do you handle duplicate keys in source during MERGE?

Question: "Your staging table has duplicate order_ids. MERGE throws an error. How do you fix it?"

Why it fails: MERGE requires each target row to match AT MOST one source row. Duplicate keys = ambiguous β†’ error.

The fix β€” deduplicate source with ROW_NUMBER:

sql
WITH deduped_source AS (
    SELECT *,
        ROW_NUMBER() OVER (
            PARTITION BY order_id           -- Group duplicates together
            ORDER BY updated_at DESC        -- Keep the LATEST record
        ) AS rn
    FROM orders_staging
)
MERGE INTO orders_fact AS t
USING (SELECT * FROM deduped_source WHERE rn = 1) AS s    -- Only latest per key
ON t.order_id = s.order_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *

Why duplicates happen in real life:

  • Kafka retry delivered the same event twice
  • Source system sent the same file twice
  • Backfill job reprocessed already-loaded data

Q09 β€” How to make MERGE faster? (6 techniques)

Question: "Our MERGE takes 3 hours on a 500 GB table. How do you optimize it?"

Why MERGE is slow: It scans the ENTIRE target to find matching records. 500 GB table + 10K source rows = reads all 500 GB just to match 10K rows.

1. Add partition column to match condition:

sql
-- BAD: Scans ALL data across ALL dates
MERGE INTO orders t USING staging s
ON t.order_id = s.order_id                              -- Scans 500 GB!

-- GOOD: Only scans matching date partitions
MERGE INTO orders t USING staging s
ON t.order_id = s.order_id AND t.order_date = s.order_date  -- Scans ~2 GB!

2. Z-ORDER or Liquid Cluster on merge key:

sql
OPTIMIZE orders ZORDER BY (order_id);
-- Data skipping now works for MERGE β€” skips irrelevant files

3. Filter unchanged rows from source:

sql
MERGE INTO orders t
USING (
    SELECT s.* FROM staging s
    LEFT JOIN orders t ON s.order_id = t.order_id
    WHERE t.order_id IS NULL OR s.hash_value != t.hash_value
) AS s
ON t.order_id = s.order_id
WHEN MATCHED THEN UPDATE SET *
WHEN NOT MATCHED THEN INSERT *

4. Broadcast small source:

python β€” editable
from pyspark.sql.functions import broadcast
target.alias("t").merge(
    broadcast(source_df).alias("s"),     # Source < 100 MB β†’ broadcast to all workers
    "t.order_id = s.order_id"
)

5. Compact target first:

sql
OPTIMIZE orders;  -- 50,000 small files β†’ 500 large files β†’ fewer tasks

6. Use Photon runtime:

  • Select "Photon" when creating cluster β€” 3-5x faster for MERGE
  • No code changes needed

Q10 β€” MERGE with Schema Evolution

Question: "Source data has a new column that doesn't exist in the target. How do you handle it?"

sql
-- Enable auto schema merge
SET spark.databricks.delta.schema.autoMerge.enabled = true;

-- MERGE as normal β€” new columns from source auto-added to target
MERGE INTO customers t
USING staging s
ON t.customer_id = s.customer_id
WHEN MATCHED THEN UPDATE SET *       -- * includes new columns
WHEN NOT MATCHED THEN INSERT *       -- New column auto-added to target schema

When to use: Source systems add new fields over time (very common). When NOT to use: Strict regulatory tables where schema changes need approval.

Q25 β€” Schema Evolution β€” All the options

Question: "How does Delta handle schema changes? What options are available?"

Option 1: Add new columns (mergeSchema):

python β€” editable
df.write.format("delta") \
    .mode("append") \
    .option("mergeSchema", "true") \
    .saveAsTable("orders")
# New columns in df are auto-added to the table schema
# Existing columns are preserved

Option 2: Replace entire schema (overwriteSchema):

python β€” editable
df.write.format("delta") \
    .mode("overwrite") \
    .option("overwriteSchema", "true") \
    .saveAsTable("orders")
# Completely replaces the schema β€” destructive!

Option 3: Rename/drop columns (requires column mapping):

sql
-- Enable column mapping first
ALTER TABLE orders SET TBLPROPERTIES ('delta.columnMapping.mode' = 'name');

-- Now you can rename and drop
ALTER TABLE orders RENAME COLUMN old_name TO new_name;
ALTER TABLE orders DROP COLUMN unused_column;

Option 4: Type widening (Delta 4.0+):

sql
-- Change column type without rewriting data
ALTER TABLE orders SET TBLPROPERTIES ('delta.enableTypeWidening' = 'true');
ALTER TABLE orders ALTER COLUMN amount TYPE DECIMAL(20,2);
-- INT β†’ BIGINT, FLOAT β†’ DOUBLE, etc. β€” NO full rewrite needed!

Answer First: Delta MERGE matches a deduplicated source to a bounded target and applies ordered update, delete, or insert clauses in one atomic commit. Match cardinality and target pruning are the main correctness and performance guardrails.

Memory Map: LAB 2 β€” Watch MERGE Work (The #1 Delta Operation) -> deduplicated staging data enters a bounded match -> matched rows update under ordered conditions -> missing keys insert new records -> table history and counts verify the lab [Delta_01_Complete_Guide.md:1646].

LAB 2 β€” Watch MERGE Work (The #1 Delta Operation)

Goal: See insert + update + delete happen in ONE atomic operation.

python β€” editable
# ─── SETUP target table ───────────────────────────
target_path = "/tmp/delta_lab/customers"
dbutils.fs.rm(target_path, recurse=True)

target_data = [
    Row(id=1, name="Alice",   status="active"),
    Row(id=2, name="Bob",     status="active"),
    Row(id=3, name="Carol",   status="active"),
]
spark.createDataFrame(target_data).write.format("delta").save(target_path)

print("πŸ“‹ TARGET (before MERGE):")
spark.read.format("delta").load(target_path).show()
# +---+-----+------+
# | id| name|status|
# +---+-----+------+
# |  1|Alice|active|
# |  2|  Bob|active|
# |  3|Carol|active|
# +---+-----+------+

# ─── Build source (daily change feed) ──────────────
source_data = [
    Row(id=2, name="Bob",     status="inactive"),   # UPDATE  (Bob deactivated)
    Row(id=3, name="Carol",   status="DELETE_ME"),  # DELETE  (special marker)
    Row(id=4, name="Dave",    status="active"),     # INSERT  (new customer)
]
source_df = spark.createDataFrame(source_data)

print("πŸ“₯ SOURCE (daily change feed):")
source_df.show()
# +---+----+---------+
# | id|name|   status|
# +---+----+---------+
# |  2| Bob| inactive|     ← will UPDATE existing row
# |  3|Carol|DELETE_ME|    ← will DELETE existing row
# |  4|Dave|   active|     ← will INSERT new row
# +---+----+---------+
python β€” editable
# ─── THE MERGE ─────────────────────────────────────
from delta.tables import DeltaTable
dt = DeltaTable.forPath(spark, target_path)

dt.alias("t").merge(
    source_df.alias("s"),
    "t.id = s.id"                                   # ← match on id
).whenMatchedDelete(
    condition = "s.status = 'DELETE_ME'"            # ← if marker, delete
).whenMatchedUpdate(
    set = {"name": "s.name", "status": "s.status"}  # ← else update
).whenNotMatchedInsert(
    values = {"id": "s.id", "name": "s.name", "status": "s.status"}  # ← new rows: insert
).execute()

print("πŸ“‹ TARGET (after MERGE):")
spark.read.format("delta").load(target_path).orderBy("id").show()
# +---+-----+--------+
# | id| name|  status|
# +---+-----+--------+
# |  1|Alice|  active|   ← unchanged
# |  2|  Bob|inactive|   ← UPDATED βœ…
# |  4| Dave|  active|   ← INSERTED βœ…
# +---+-----+--------+
#                         ← Carol (id=3) DELETED βœ…

# ─── Verify: one commit for the whole thing ────────
dt.history().select("version", "operation", "operationMetrics").show(truncate=False)
# +-------+---------+─────────────────────────────────────────────────┐
# |version|operationβ”‚ operationMetrics                                 β”‚
# +-------+---------+──────────────────────────────────────────────────
# |   1   β”‚ MERGE   β”‚ numTargetRowsInserted=1                          β”‚
# |       β”‚         β”‚ numTargetRowsUpdated=1                           β”‚
# |       β”‚         β”‚ numTargetRowsDeleted=1                           β”‚
# |   0   β”‚ WRITE   β”‚ numOutputRows=3                                  β”‚
# +-------+---------+β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
#
# 🎯 ONE atomic commit handled INSERT + UPDATE + DELETE together.
#    That's why MERGE is the most important Delta operation.

Answer First: Why it happens: Your source has 2+ rows with the same join key β†’ Delta can't decide which one wins.

Memory Map: MERGE with Duplicate Keys in Source -> multiple rows share one target key -> engine cannot choose an update winner -> ranking or aggregation reduces the source first -> precommit uniqueness check prevents ambiguity [Delta_01_Complete_Guide.md:1965].

Gotcha 4: MERGE with Duplicate Keys in Source

Error: UnsupportedOperationException: Cannot perform Merge as multiple source
rows matched and attempted to modify the same target row.

Why it happens: Your source has 2+ rows with the same join key β†’ Delta can't decide which one wins.

Fix:

python β€” editable
from pyspark.sql.window import Window
from pyspark.sql.functions import row_number, desc

# Deduplicate source BEFORE merging
w = Window.partitionBy("id").orderBy(desc("updated_at"))
source_deduped = source_df.withColumn("rn", row_number().over(w)) \
                          .filter("rn = 1") \
                          .drop("rn")
# Now source has ONE row per id β€” merge will succeed

Interview trap: This is THE most common MERGE production bug. If you know this fix, you sound senior.

Databricks-routed Delta concepts: 04_ETL_Scenarios_and_Design

Q25: Scenario β€” Your MERGE statement takes 3 hours. How do you optimize it?

Simple Explanation: A slow MERGE usually means the target table is too large, not well organized, or the source is not pre-filtered. The fix is systematic: profile first, then apply targeted optimizations.

Answer:

Diagnosis Checklist:
β”Œβ”€ Check Spark UI for the MERGE job
β”‚ β”œβ”€β”€ Which stage is slowest?
β”‚ β”œβ”€β”€ Is there a broadcast happening (or should there be)?
β”‚ β”œβ”€β”€ Data skew in join keys?
β”‚ └── How many files in target table?
β”‚
β”œβ”€β”€ Common Causes & Fixes:
β”‚ β”œβ”€β”€ Too many small files in target
β”‚ β”‚ └── Fix: OPTIMIZE target table
β”‚ β”‚
β”‚ β”œβ”€β”€ No partition pruning
β”‚ β”‚ └── Fix: Add partition column to MERGE condition
β”‚ β”‚ MERGE INTO target t USING source s
β”‚ β”‚ ON t.id = s.id AND t.date = s.date -- date is partition column
β”‚ β”‚
β”‚ β”œβ”€β”€ No data skipping on merge key
β”‚ β”‚ └── Fix: OPTIMIZE target ZORDER BY (merge_key)
β”‚ β”‚
β”‚ β”œβ”€β”€ Source too large (merging more than needed)
β”‚ β”‚ └── Fix: Pre-filter source to only changed records
β”‚ β”‚
β”‚ β”œβ”€β”€ Both tables are large (no broadcast)
β”‚ β”‚ └── Fix: If source < 1 GB, force broadcast(source)
β”‚ β”‚
β”‚ β”œβ”€β”€ Not using Photon
β”‚ β”‚ └── Fix: Switch to Photon runtime (can be 3-5x faster for MERGE)
β”‚ β”‚
β”‚ └── Data skew in merge key
β”‚ └── Fix: Salt the merge key (complex but effective)
β”‚
└── Expected result: 3 hoursβ†’20-40 minutes

Interview Tip: Walk through the diagnosis checklist step by step. Say "First I check the Spark UI, then I look at file count, then partition pruning, then data skipping. The fix depends on the bottleneck." Methodical > guessing.

What NOT to Say: "Just add more workers." More workers do not help if the bottleneck is data skew (one worker gets 80% of the data) or small files (10,000 files = 10,000 tasks of 1 KB each).

Databricks-routed Delta concepts: DB_02_ETL_Pipelines_Databricks

Q17: Scenario β€” Our MERGE on booking fact table takes 3 hours. How do you fix it?

Simple Explanation: This is a debugging scenario. The approach: first INVESTIGATE (why is it slow?), then FIX (apply optimizations in order of impact).

sql
-- STEP 1: INVESTIGATE β€” check the table's health
DESCRIBE DETAIL fact_bookings;
-- Key things to look at:
-- numFiles: 50,000 β†’ TOO MANY! Small file problem (should be ~500 files for 2 TB)
-- sizeInBytes: 2 TB β†’ That's the data size. 50K files means avg file is only 40 MB (should be ~1 GB)
sql
-- STEP 2: FIX (in order of impact β€” do #1 first, then check if it's fast enough)

-- Fix 1: Compact files (biggest impact if you have small file problem)
OPTIMIZE fact_bookings;
-- Combines 50,000 small files β†’ ~500 large files
-- MERGE now creates ~500 tasks instead of 50,000 β†’ much faster

-- Fix 2: Add partition column to MERGE ON clause (partition pruning)
MERGE INTO fact_bookings t USING staging s
ON t.booking_id = s.booking_id
   AND t.booking_date = s.booking_date;
-- booking_date = partition column β†’ MERGE only scans today's data, not all 2 TB

-- Fix 3: Z-ORDER or Liquid Clustering on the merge key
OPTIMIZE fact_bookings ZORDER BY (booking_id);
-- Groups similar booking_ids together β†’ data skipping works better during MERGE
-- OR for new tables: ALTER TABLE fact_bookings CLUSTER BY (booking_date, booking_id);

-- Fix 4: Switch to Photon runtime (3-5x faster MERGE, no code changes)
-- Just change the cluster runtime to Photon in the job configuration

-- Fix 5: Filter source to only merge rows that actually changed
-- (See Day 1, Q7 for the full technique)

-- Fix 6: Prevent future small files
ALTER TABLE fact_bookings SET TBLPROPERTIES (
    'delta.autoOptimize.optimizeWrite' = 'true',   -- Coalesce on write
    'delta.autoOptimize.autoCompact' = 'true'       -- Auto-compact after writes
);

Databricks-routed Delta concepts: DB_02_Quick_Recall

⚠️ Q42MERGE takes 3 hours on fact table. How to fix?

5-step fix:

  1. Liquid Clustering on merge key (biggest win β€” scans only relevant files)
  2. Filter source to only today's changes (don't MERGE entire history)
  3. OPTIMIZE target table first (compact small files)
  4. Enable low shuffle merge: spark.databricks.delta.merge.lowShuffle.enabled = true
  5. Check for data skew: one key has millions of rows β†’ use salting

Databricks-routed Delta concepts: DB_04_Production_CICD_MockInterview

MOCK Q4: "Our booking fact table MERGE takes 3 hours. How would you optimize?"

Systematic approach (don't jump to solutions β€” investigate first):

  1. DESCRIBE DETAIL β†’ Check numFiles (small file problem?) and sizeInBytes
  2. OPTIMIZE β†’ Compact small files into ~1 GB files
  3. Add partition column to MERGE ON clause β†’ partition pruning
  4. Z-ORDER on merge key β†’ better data skipping
  5. Filter source β†’ only MERGE rows that actually changed
  6. Enable Photon β†’ 3-5x faster MERGE
  7. Consider Liquid Clustering β†’ automatic, incremental optimization
  8. Enable autoOptimize for the future

Interview tip: Always start with diagnosis, not solutions. "Before optimizing, I'd check the table health: DESCRIBE DETAIL to see file count and size, then look at the Spark UI to see where the time is spent." Then walk through fixes in order of impact.

What NOT to say: "Just add more workers." That might help, but it doesn't address root causes like small files, missing partition pruning, or data skew. Also don't say "Rewrite the MERGE as DELETE + INSERT" β€” MERGE is the correct approach for upserts, and the optimization is in how you structure the ON clause and target table.

Routed Delta question-bank prompts β€” 20-merge-schema-evolution

6. What is schema enforcement in Delta Lake?

Delta Lake rejects writes that don't match the table's schema (wrong column names, types, or missing required columns). It prevents silent data corruption by failing the write immediately.

7. What is schema evolution and how do you enable it?

Allowing the table schema to change over time (add columns, widen types). Enable with .option("mergeSchema", "true") on write or ALTER TABLE SET TBLPROPERTIES ('delta.columnMapping.mode' = 'name') for renames.

19. What are table constraints in Delta Lake (CHECK, NOT NULL)? > Declarative rules enforced at write time. NOT NULL prevents null values in a column. CHECK constraints enforce arbitrary conditions (e.g., CHECK (fare > 0)). Writes that violate constraints are rejected.

8. Compare schema enforcement vs schema evolution β€” give an example where each is appropriate.

Key points: Enforcement = reject writes that don't match (default). Evolution = allow schema changes on write. Enforcement example: regulatory Gold table where schema changes need approval. Evolution example: Bronze ingestion from a source system that occasionally adds new columns β€” use mergeSchema to accept new columns automatically.

6. Explain how MERGE INTO works internally. What are the performance implications of a full table scan in MERGE?

Key points: MERGE reads the source, finds matching rows in the target (via the ON clause), then applies MATCHED/NOT MATCHED actions. Without partition pruning, MERGE scans the ENTIRE target table to find matches β€” O(n) on table size. Optimize by: (1) include partition column in ON clause for pruning, (2) Z-ORDER/Liquid Clustering on merge key for data skipping, (3) filter source to only changed rows, (4) OPTIMIZE target to reduce file count.

12. Explain the difference between Copy-on-Write and Merge-on-Read in Delta Lake. > Key points: Copy-on-Write (default pre-Deletion Vectors): UPDATE/DELETE rewrites the entire affected Parquet file. Fast reads, slow writes. Merge-on-Read (with Deletion Vectors): marks rows as deleted in a bitmap, reads merge the bitmap at read time. Fast writes, slightly slower reads. Periodic compaction merges the deletions. Choose based on read-vs-write frequency.

Advanced

Delta Time Travel, CDF, and Row-Level Features

#

Delta Time Travel, CDF, and Row-Level Features

Answer First: Time travel reconstructs retained snapshots, while Change Data Feed exposes committed row changes for downstream incremental processing; both depend on retained history.

Memory Map: commit history -> retained files -> snapshot or row changes -> downstream recovery.

SECTION 3: TIME TRAVEL & RECOVERY

Answer First: Because Delta keeps a diary of every change (the transaction log), you can "rewind" and see your data at any previous point.

Memory Map: time travel work? Show all query methods -> version or timestamp resolves a historical snapshot -> checkpoint plus later actions reconstruct active files -> reader scans that immutable state -> retention determines continued availability [03_Delta_Lake_and_Lakehouse.md:393].

Q13: How does time travel work? Show all query methods.

Simple Explanation: Because Delta keeps a diary of every change (the transaction log), you can "rewind" and see your data at any previous point. Like pressing rewind on a video β€” "what did this table look like 3 days ago?" This works because old Parquet files are kept around (until VACUUM deletes them).

Answer:

sql
-- By version number
SELECT * FROM orders VERSION AS OF 5;
SELECT * FROM orders@v5;

-- By timestamp
SELECT * FROM orders TIMESTAMP AS OF '2025-01-15 10:30:00';

-- Check history first
DESCRIBE HISTORY orders;

-- Restore entire table
RESTORE TABLE orders TO VERSION AS OF 5;
RESTORE TABLE orders TO TIMESTAMP AS OF '2025-01-15';

PySpark:

python β€” editable
# By version
df = spark.read.format("delta").option("versionAsOf", 5).load(path)

# By timestamp
df = spark.read.format("delta").option("timestampAsOf", "2025-01-15").load(path)

Answer First: Normally, deleting 1 row from a 1 GB file means rewriting the ENTIRE 1 GB file. That's like reprinting a 500-page book because of one typo. Deletion Vectors just stick a Post-it note on the page saying "ignore this line." The actual reprint happens later during OPTIMIZE.

Memory Map: Deletion Vectors? How do they improve UPDATE/DELETE performance -> delete writes row-position markers instead of replacing large files -> scan combines markers with columnar data -> write latency falls while read work can rise -> compaction later removes marked rows [03_Delta_Lake_and_Lakehouse.md:541].

Q17: What are Deletion Vectors? How do they improve UPDATE/DELETE performance?

Simple Explanation: Normally, deleting 1 row from a 1 GB file means rewriting the ENTIRE 1 GB file. That's like reprinting a 500-page book because of one typo. Deletion Vectors just stick a Post-it note on the page saying "ignore this line." The actual reprint happens later during OPTIMIZE.

Answer: Instead of rewriting entire Parquet files for DELETE/UPDATE/MERGE, deletion vectors mark individual rows as deleted in a separate lightweight file.

Without deletion vectors:

  • DELETE 1 row from a 1 GB Parquet file β†’ rewrite entire 1 GB file
  • MERGE affecting 100 files β†’ rewrite all 100 files

With deletion vectors:

  • DELETE 1 row β†’ write a tiny deletion vector file (~bytes)
  • Reads filter out deleted rows using the deletion vector
  • Physical rewrite is deferred to next OPTIMIZE
sql
-- Enable deletion vectors
ALTER TABLE my_table SET TBLPROPERTIES ('delta.enableDeletionVectors' = 'true');

Trade-off: Slightly slower reads (must apply deletion vectors) but much faster writes. OPTIMIZE reclaims space.

Answer First: A deep clone copies table data and metadata into an independent target, while a shallow clone initially references source data files. Retention and source-lifecycle requirements determine whether that dependency is safe.

Memory Map: clone operations? Explain deep clone vs shallow clone -> source snapshot fixes the starting state -> shallow reference or deep copy chooses ownership -> target inherits dependency or independence -> source-retention test reveals lifecycle [03_Delta_Lake_and_Lakehouse.md:617].

Q20: What are clone operations? Explain deep clone vs shallow clone.

Answer:

AspectDeep CloneShallow Clone
Copies data?YES (full copy)NO (references source files)
Independent?Yes β€” fully independentNo β€” depends on source data files
SpeedSlow (copies all data)Fast (copies only metadata)
Use caseProduction copies, migrationTesting, experimentation, quick snapshots
VACUUM safe?YesNO β€” vacuuming source can break clone
sql
-- Deep clone (full copy)
CREATE TABLE orders_backup DEEP CLONE orders;

-- Shallow clone (metadata only)
CREATE TABLE orders_test SHALLOW CLONE orders;

-- Incremental deep clone (only new changes since last clone)
CREATE TABLE orders_backup DEEP CLONE orders;  -- subsequent runs are incremental

Answer First: Delta Sharing is an open protocol for secure data sharing across organizations.

Memory Map: Delta Sharing? How does it work -> delta sharing it work defines provider ownership and recipient access semantics -> provider selects governed objects -> share grants recipient access -> credentialed client reads current data -> audit and revocation preserve provider control [03_Delta_Lake_and_Lakehouse.md:767].

Q26: What is Delta Sharing? How does it work?

Answer: Delta Sharing is an open protocol for secure data sharing across organizations.

How it works:

  1. Provider shares a Delta table via a Delta Sharing Server
  2. Provider generates a sharing profile (JSON with endpoint + credentials)
  3. Recipient uses any client (Spark, pandas, Power BI) to read shared data
  4. Data is read directly from the provider's storage β€” no copying
  5. Recipient gets read-only access with the provider's access controls
python β€” editable
# Recipient reads shared data
df = spark.read.format("deltaSharing").load("profile.json#share.schema.table")

Key benefits:

  • No data copying (cost-effective)
  • Open protocol (not Databricks-specific)
  • Audit logging on provider side
  • Fine-grained access control

Answer First: Normally, when you DELETE or UPDATE even a single row in a Parquet file, Delta has to rewrite the ENTIRE file. If the file is 1 GB, Delta writes a new 1 GB file just to remove one row. This is very expensive.

Memory Map: Deletion Vectors -> bitmap-like metadata identifies removed positions -> original data file remains physically present -> compatible readers suppress marked records -> rewrite maintenance eventually materializes changes [DB_01_Delta_Lake_Deep_Dive.md:463].

Q11: What are Deletion Vectors?

Simple Explanation: Normally, when you DELETE or UPDATE even a single row in a Parquet file, Delta has to rewrite the ENTIRE file. If the file is 1 GB, Delta writes a new 1 GB file just to remove one row. This is very expensive.

Deletion Vectors solve this by creating a tiny separate file that says "row #47 in file X is deleted." The original file stays untouched. When reading, Delta checks the deletion vector and skips that row. The actual file rewrite happens later during OPTIMIZE (not immediately).

Real-world analogy: Instead of reprinting an entire 500-page book because of one typo, you just stick a Post-it note on the page saying "ignore this line." The actual reprint happens later when convenient.

  • Without Deletion Vectors: DELETE 1 row from 1 GB file β†’ rewrite entire 1 GB file (slow)
  • With Deletion Vectors: DELETE 1 row β†’ write tiny marker file (~bytes) (fast!)
  • Cleanup: OPTIMIZE will do the actual file rewrite later
sql
-- Enable deletion vectors on a table
ALTER TABLE bookings SET TBLPROPERTIES (
    'delta.enableDeletionVectors' = 'true'    -- Tells Delta: use DV for this table
);
-- After this, DELETEs and UPDATEs become much faster
-- Reads are slightly slower (must check deletion vectors) β€” but usually worth it

Trade-off: Writes get much faster, reads get slightly slower. Run OPTIMIZE periodically to clean up.

New in Delta 4.1 (2025): You can enable deletion vectors without blocking concurrent writes (conflict-free enablement).

SECTION 4: TIME TRAVEL & RECOVERY (30 min)

Q13: What is Time Travel? How to query old versions of a table?

Simple Explanation: Because Delta keeps a log of every change (the transaction log), you can "go back in time" and see what the data looked like at any previous point. This is called Time Travel.

Why is it useful?

  • Debugging: "The report showed wrong numbers yesterday β€” let me check yesterday's data"
  • Recovery: "Someone accidentally deleted 1000 bookings β€” let me restore them"
  • Auditing: "What did the passenger table look like before the migration?"
sql
-- METHOD 1: Query by version number
-- (Every commit gets a version: 0, 1, 2, 3, ...)
SELECT * FROM bookings VERSION AS OF 5;     -- See table as it was at version 5
SELECT * FROM bookings@v5;                   -- Shorthand for the same thing

-- METHOD 2: Query by timestamp
-- (Go back to a specific date/time)
SELECT * FROM bookings TIMESTAMP AS OF '2026-03-15 10:30:00';

-- See full history of all changes
DESCRIBE HISTORY bookings;
-- Shows: version, timestamp, operation (INSERT/DELETE/MERGE), user, metrics

-- RESTORE: Roll back the entire table to a previous version
RESTORE TABLE bookings TO VERSION AS OF 5;
-- WARNING: This creates a NEW version (not destructive) β€” you can undo the restore too!

Limits to remember:

  • Default data retention: 7 days β€” can't time travel beyond this (VACUUM deletes old files)
  • Default log retention: 30 days β€” DESCRIBE HISTORY works for 30 days
  • VACUUM breaks time travel for vacuumed versions

SECTION 4: TIME TRAVEL & RECOVERY

🧠 Memory Map: Time Travel

🧠 TIME TRAVEL = "Read old versions of a table"
TIME TRAVEL"Read old versions of a table"
Two ways to travel back:
VERSION: SELECT * FROM bookings VERSION AS OF 5;
TIME: SELECT * FROM bookings TIMESTAMP AS OF '2026-03-20';
DESCRIBE HISTORY"See all versions and what changed"
DESCRIBE HISTORY bookings;
Shows: version, timestamp, operation, who did it
RESTORE"Undo mistakes"
RESTORE TABLE bookings TO VERSION AS OF 5;
⚠️This creates a NEW version (doesn't delete history)
Remember: "VTR" = Version, Timestamp, Restore
⚠️TRAP: Time travel only works within VACUUM retention period!
Default = 7 days. After VACUUM runs→old versions are GONE forever.

SECTION 3: Deletion Vectors -- Internal Mechanics

Answer First: Deletion vectors mark removed row positions without immediately rewriting the containing Parquet file. Reads apply the bitmap, and later maintenance can materialize the change by rewriting files.

Memory Map: Deletion Vectors? How do they work internally -> protocol feature enables row-position deletion metadata -> transaction action references the marker -> snapshot reader applies it to the referenced file -> reorganization can purge obsolete rows [DB_06_Delta_Lake_Advanced_Masterclass.md:304].

Q7: What are Deletion Vectors? How do they work internally?

Answer:

The problem Deletion Vectors solve:

Without Deletion Vectors (traditional Delta behavior):

πŸ“‹ Overview
DELETE FROM bookings WHERE booking_id = 'ABC123'
Step 1: Find which Parquet file contains 'ABC123'
(File X: 1,000,000 rows, ABC123 is row #547,832)
Step 2: Read entire File X (1 GB)
Step 3: Filter out the 1 row to delete
Step 4: Rewrite 999,999 rows as a NEW Parquet file (File Y, ~1 GB)
Step 5: Transaction log: remove File X, add File Y
Result: 1 GB read + 1 GB write -- just to delete 1 row!

With Deletion Vectors:

πŸ“‹ Overview
DELETE FROM bookings WHERE booking_id = 'ABC123'
Step 1: Find which Parquet file contains 'ABC123'
(File X: 1,000,000 rows, ABC123 is row #547,832)
Step 2: Create a tiny "deletion vector" file (a bitmap)
The bitmap has 1,000,000 bits. Set bit #547,832 to 1 (deleted).
Size: ~122 KB (1M bits / 8 = 125 KB)
Step 3: Transaction log: add deletion vector for File X
Result: No data file rewrite! Just a tiny bitmap file.

Internal structure of a Deletion Vector:

πŸ—‚οΈDeletion Vector = RoaringBitmap
Stored as a separate small file alongside data files
OR stored inline in the transaction log (for very small DVs)
Format: RoaringBitmap -- compressed bitmap, extremely space-efficient
Each bit position = a row number in the corresponding Parquet file
Bit = 0: row is active (not deleted)
Bit = 1: row is deleted (soft-deleted)
Multiple DVs can exist for the same file (from multiple DELETE operations)

How reads work with Deletion Vectors:

πŸ“‹ Overview
SELECT * FROM bookings WHERE origin = 'LHR'
Step 1: Identify relevant Parquet files via data skipping
Step 2: For each file, check if a deletion vector exists
Step 3: If DV exists:
a. Read the Parquet file normally
b. Apply the DV bitmap as a filter -- skip deleted rows
c. Return only non-deleted rows
Step 4: If no DV exists: Read normally (no overhead)
Read overhead: Very minimal -- bitmap lookup is O(1) per row

Answer First: Interview insight: "Deletion Vectors trade write performance for read performance. They make writes fast (no rewrite) but reads slightly slower (bitmap filtering). OPTIMIZE is the mechanism that rebalances -- it purges DVs by rewriting files without the deleted rows."

Memory Map: the performance implications of Deletion Vectors? When do they help vs hurt -> avoided file rewrites accelerate sparse mutations -> extra metadata adds read-side filtering -> dense changes can erase the benefit -> workload and compaction metrics decide suitability [DB_06_Delta_Lake_Advanced_Masterclass.md:367].

Q8: What are the performance implications of Deletion Vectors? When do they help vs hurt?

Answer:

When DVs help dramatically (the sweet spot):

Scenario: Frequent small DELETEs or UPDATEs on a large table
Without DVs:
Each DELETE rewrites entire Parquet files (1 GB each)
100 deletes/day * 1 GB rewrite each = 100 GB of write amplification/day
With DVs:
Each DELETE creates a tiny bitmap file (~KB)
100 deletes/day = ~100 KB of overhead/day
Write amplification reduced by ~1000x

When DVs accumulate and hurt reads:

Problem: Table has been running for months with DVs, never compacted
File X (1 GB, 10M rows) has:
DV from Jan: 50,000 rows marked deleted
DV from Feb: 80,000 rows marked deleted
DV from Mar: 120,000 rows marked deleted
Total: 250,000 rows are "soft deleted" (2.5% of file)
Read penalty:
Engine must read ALL 10M rows from Parquet
Then apply 3 DV bitmaps to filter out 250K rows
Net useful data: 9.75M rows, but you read 10M + 3 bitmap files
Wasted I/O: ~2.5% (manageable)
But if 50% of rows are deleted via DVs:
Read 10M rows, throw away 5M -> 50% wasted I/O
THIS IS BAD -- you need to run OPTIMIZE to purge DVs

How OPTIMIZE purges Deletion Vectors:

OPTIMIZE my_table
For files with DVs:
1. Read data file + its deletion vector(s)
2. Write ONLY the non-deleted rows to a new file
3. No DV needed for the new file -- deleted rows are gone
4. Transaction log: remove old file + old DV, add new clean file

Interview insight: "Deletion Vectors trade write performance for read performance. They make writes fast (no rewrite) but reads slightly slower (bitmap filtering). OPTIMIZE is the mechanism that rebalances -- it purges DVs by rewriting files without the deleted rows."

Table property to enable/disable:

sql
-- Enable deletion vectors (default in Databricks Runtime 14.x+)
ALTER TABLE my_table SET TBLPROPERTIES ('delta.enableDeletionVectors' = true);

-- Check if DVs are enabled
DESCRIBE DETAIL my_table;
-- Look for 'deletionVectors' in table properties

SECTION 4: Change Data Feed (CDF) -- CDC with Delta Lake

Answer First: Change Data Feed (CDF) is a Delta Lake feature that records what changed (not just the final state) for each commit. It captures row-level changes with change types: INSERT, UPDATE (pre-image and post-image), and DELETE.

Memory Map: Change Data Feed? How does it work internally -> change data feed it work internally defines a bounded change-consumption path -> enabled version range selects commits -> typed row changes carry commit metadata -> consumer checkpoints progress -> source-to-sink reconciliation proves delivery [DB_06_Delta_Lake_Advanced_Masterclass.md:476].

Q10: What is Change Data Feed? How does it work internally?

Answer:

Change Data Feed (CDF) is a Delta Lake feature that records what changed (not just the final state) for each commit. It captures row-level changes with change types: INSERT, UPDATE (pre-image and post-image), and DELETE.

Enabling CDF:

sql
-- On a new table:
CREATE TABLE bookings (...) USING DELTA
TBLPROPERTIES ('delta.enableChangeDataFeed' = true);

-- On an existing table:
ALTER TABLE bookings SET TBLPROPERTIES ('delta.enableChangeDataFeed' = true);
-- NOTE: CDF only captures changes AFTER it is enabled. It is NOT retroactive.

What CDF records internally:

πŸ“ Architecture Diagram
When you run: UPDATE bookings SET status='CANCELLED' WHERE id='ABC123'

Without CDF:
- Delta just rewrites the file with the new value
- No record of what the old value was
- Transaction log shows: remove old_file, add new_file

With CDF:
- Delta writes the change to a special _change_data/ directory:

bookings/
β”œβ”€β”€ _delta_log/
β”‚   └── 00000000000000000042.json
β”œβ”€β”€ _change_data/                          <-- CDF data lives here
β”‚   └── cdc-00000-42.snappy.parquet       <-- Change records for commit 42
β”‚       Contains:
β”‚       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚       β”‚ id       β”‚ status   β”‚ _change_type      β”‚ _commit_version  β”‚
β”‚       β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚       β”‚ ABC123   β”‚ ACTIVE   β”‚ update_preimage   β”‚ 42               β”‚
β”‚       β”‚ ABC123   β”‚ CANCELLEDβ”‚ update_postimage  β”‚ 42               β”‚
β”‚       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”œβ”€β”€ part-00000-abc123.snappy.parquet
└── ...

The four change types:

_change_typeMeaningWhen generated
insertNew row was addedINSERT operation
update_preimageRow BEFORE it was changedUPDATE (the old values)
update_postimageRow AFTER it was changedUPDATE (the new values)
deleteRow was removedDELETE operation

Answer First: Change Data Feed emits row-level inserts, updates, and deletes for enabled table versions so downstream jobs can process only committed changes. Consumers must track versions and stay within the table’s retained history.

Memory Map: you read Change Data Feed? What are the query patterns -> batch reader requests starting and ending versions -> streaming reader advances from a checkpointed offset -> commit metadata labels each emitted change -> consumer reconciliation protects progress [DB_06_Delta_Lake_Advanced_Masterclass.md:533].

Q11: How do you read Change Data Feed? What are the query patterns?

Answer:

python β€” editable
# Method 1: Read changes between two versions
changes_df = (spark.read
    .format("delta")
    .option("readChangeFeed", "true")
    .option("startingVersion", 10)
    .option("endingVersion", 20)
    .table("bookings")
)

# Method 2: Read changes from a specific timestamp
changes_df = (spark.read
    .format("delta")
    .option("readChangeFeed", "true")
    .option("startingTimestamp", "2026-03-01T00:00:00Z")
    .option("endingTimestamp", "2026-03-15T23:59:59Z")
    .table("bookings")
)

# Method 3: Streaming -- read changes as a continuous stream
stream_df = (spark.readStream
    .format("delta")
    .option("readChangeFeed", "true")
    .option("startingVersion", 0)
    .table("bookings")
)

# The DataFrame has extra columns:
# _change_type: insert, update_preimage, update_postimage, delete
# _commit_version: which version of the table this change belongs to
# _commit_timestamp: when the change was committed

Real-world CDC pipeline pattern:

python β€” editable
# Propagate changes from bronze -> silver -> gold
# This is the pattern interviewers want to see

# 1. Read changes from the bronze layer since last processed version
bronze_changes = (spark.read.format("delta")
    .option("readChangeFeed", "true")
    .option("startingVersion", last_processed_version)
    .table("bronze.raw_bookings")
)

# 2. Apply business logic (only process inserts and post-images)
silver_changes = (bronze_changes
    .filter("_change_type != 'update_preimage'")  # Don't need old values for silver
    .withColumn("processed_at", current_timestamp())
    .withColumn("is_deleted", col("_change_type") == "delete")
)

# 3. MERGE into silver table
silver_changes.createOrReplaceTempView("changes")
spark.sql("""
    MERGE INTO silver.bookings t
    USING changes s ON t.booking_id = s.booking_id
    WHEN MATCHED AND s.is_deleted = true THEN DELETE
    WHEN MATCHED THEN UPDATE SET *
    WHEN NOT MATCHED AND s.is_deleted = false THEN INSERT *
""")

Answer First: Change Data Feed emits row-level inserts, updates, and deletes for enabled table versions so downstream jobs can process only committed changes. Consumers must track versions and stay within the table’s retained history.

Memory Map: the gotchas with Change Data Feed -> retention can remove requested history -> initial snapshot semantics may surprise consumers -> updates emit preimage and postimage rows -> durable checkpoint and idempotent sink prevent gaps [DB_06_Delta_Lake_Advanced_Masterclass.md:603].

Q12: What are the gotchas with Change Data Feed?

Answer -- interviewers love these:

Gotcha 1: CDF is NOT retroactive

You enable CDF on a table that already has 100 versions.
CDF only records changes from version 101 onward.
You CANNOT query CDF for versions 1-100.

Gotcha 2: CDF and OPTIMIZE/VACUUM interaction

CDF data lives in _change_data/ directory.
VACUUM does NOT clean up _change_data/ files by default.
But if you VACUUM with a very short retention period,
you might lose the ability to query old CDF changes.
Specifically: The change data files follow the same retention
policy as the table. After VACUUM, versions older than
the retention period are gone -- including their CDF data.

Gotcha 3: INSERT OVERWRITE does not generate CDF

sql
-- This DOES generate CDF:
INSERT INTO bookings VALUES (...)
DELETE FROM bookings WHERE ...
UPDATE bookings SET ...
MERGE INTO bookings ...

-- This does NOT generate CDF (or generates it differently):
INSERT OVERWRITE bookings SELECT * FROM ...
-- INSERT OVERWRITE is treated as: delete all + insert all
-- CDF records: deletes for ALL old rows + inserts for ALL new rows
-- This can produce a MASSIVE CDF output for large tables

Gotcha 4: CDF storage overhead

Every UPDATE stores TWO rows in CDF (pre-image + post-image).
For a table with millions of updates per day, CDF can use
significant storage:
1M updates/day * 2 rows each * 1 KB/row = ~2 GB/day in CDF data
Over a year: ~730 GB just for CDF
Plan for this in your storage budget.

Gotcha 5: Streaming CDF with schema evolution

If the source table's schema changes (new columns added),
existing streaming CDF readers may fail or miss the new columns.
You need to restart the stream with schema evolution options:
.option("mergeSchema", "true")

SECTION 5: UniForm -- Universal Format

Answer First: The data science team uses Apache Spark with Iceberg.

Memory Map: Delta UniForm? Why was it created -> Delta commits remain the writable authority -> asynchronous conversion publishes compatible metadata -> read-only Iceberg clients query the same data files -> converted Delta version reveals metadata freshness [DB_06_Delta_Lake_Advanced_Masterclass.md:663].

Q13: What is Delta UniForm? Why was it created?

Answer:

The problem: Your organization uses Delta Lake in Databricks, but:

  • The data science team uses Apache Spark with Iceberg
  • The analytics team uses Presto/Trino with Hudi
  • A partner company uses Snowflake which reads Iceberg
  • Your streaming team uses Flink which supports Iceberg natively

Each team wants to read the SAME data but each requires a different table format's metadata. Without UniForm, you would need to maintain multiple copies of the same data in different formats.

UniForm solution: Delta Lake writes its native Delta metadata AND simultaneously generates metadata compatible with other formats (Iceberg and Hudi).

πŸ—‚οΈTraditional (without UniForm):
my_table/
_delta_log/ <-- Delta metadata (only Delta readers can use this)
With UniForm enabled:
my_table/
_delta_log/ <-- Delta metadata (Databricks, Spark, Delta readers)
metadata/ <-- Iceberg metadata (Trino, Flink, Snowflake, etc.)
v1.metadata.json
snap-123.avro
...
(Hudi metadata) <-- Hudi metadata (if Hudi compatibility is enabled)

How to enable UniForm:

sql
-- Enable Iceberg compatibility:
CREATE TABLE my_table (...) USING DELTA
TBLPROPERTIES (
    'delta.universalFormat.enabledFormats' = 'iceberg'
);

-- Enable on existing table:
ALTER TABLE my_table SET TBLPROPERTIES (
    'delta.universalFormat.enabledFormats' = 'iceberg'
);

-- Enable both Iceberg and Hudi:
ALTER TABLE my_table SET TBLPROPERTIES (
    'delta.universalFormat.enabledFormats' = 'iceberg,hudi'
);

Answer First: UniForm keeps Delta as the writable authority and asynchronously generates compatible Iceberg or Hudi metadata after Delta commits. External Iceberg clients are read-only, converted metadata can lag the latest Delta version, and some Delta features are not representable through every external reader.

Memory Map: the limitations and gotchas of UniForm -> Delta transaction commits successfully before conversion -> asynchronous generation can group or lag commits -> read-only clients consume only supported Iceberg metadata -> converted_delta_version and manual sync diagnose freshness [DB_06_Delta_Lake_Advanced_Masterclass.md:713].

Q14: What are the limitations and gotchas of UniForm?

Current guardrail: Iceberg client access through UniForm is read-only. Metadata generation is asynchronous, so monitor converted_delta_version; a concurrent conversion does not block a new Delta commit. See Databricks UniForm documentation.

Answer:

Limitation 1: Write overhead

Every Delta commit now also generates Iceberg metadata.
Write latency increases by 5-15% due to the extra metadata generation.
For streaming tables with sub-second latency requirements, this matters.

Limitation 2: Feature asymmetry

πŸ“ Architecture Diagram
Not all Delta features have equivalents in Iceberg/Hudi:

Feature                      | Delta | Iceberg via UniForm
─────────────────────────────┼───────┼────────────────────
Deletion Vectors             | Yes   | Yes (mapped to Iceberg delete files)
Liquid Clustering            | Yes   | Partially (Iceberg sees partitioning)
Change Data Feed             | Yes   | No (CDF is Delta-specific)
Time Travel (arbitrary)      | Yes   | Limited (Iceberg snapshots only)
Column Mapping               | Yes   | Yes
Schema Evolution             | Yes   | Yes (with some type limitations)
CHECK Constraints            | Yes   | No
Generated Columns            | Yes   | No

Limitation 3: Catalog requirements

UniForm Iceberg metadata needs a catalog (Unity Catalog, Hive Metastore,
or REST catalog) so that Iceberg readers know where to find the metadata.
The catalog mapping must be configured correctly on both the Delta writer
side and the Iceberg reader side.

Limitation 4: One-way synchronization

UniForm is WRITE from Delta, READ from Iceberg.
You CANNOT write to the table using Iceberg clients.
The Delta side is the single source of truth.
If an Iceberg client tries to write, it will corrupt the table
or the writes will be ignored by Delta.

Interview question: "If your company uses Databricks but a partner uses Snowflake, how do you share data?"

  • Answer 1 (best): Enable UniForm with Iceberg format. Snowflake reads the Iceberg metadata natively. Zero data duplication.
  • Answer 2: Delta Sharing (share Delta tables directly -- Snowflake has a Delta Sharing connector).
  • Answer 3 (worst): Export to Parquet files. Manual, error-prone, not real-time.

Q28: What is the difference between Shallow Clone and Deep Clone?

Answer:

πŸ—‚οΈDEEP CLONE:
CREATE TABLE my_clone DEEP CLONE source_table;
Copies ALL data files (full physical copy)
Copies ALL metadata (schema, properties, partition info)
Independent of source -- changes to source don't affect clone
Use for: production backups, creating test environments, migration
Cost: Full storage duplication (if source is 10 TB, clone is 10 TB)
Time: Proportional to table size (can take hours for large tables)
SHALLOW CLONE:
CREATE TABLE my_clone SHALLOW CLONE source_table;
Copies ONLY metadata (no data files copied!)
Clone REFERENCES the source table's data files
Much faster to create (seconds, not hours)
Cost: Minimal (just metadata)
But: If source runs VACUUM, shared files get deleted -> clone breaks!
Writes to clone create NEW files (copy-on-write for the clone)
Use for: quick experimentation, testing, short-lived environments
TRAP QUESTION: "What happens if I VACUUM the source of a shallow clone?"
Answer: The clone loses access to the deleted files. Queries on the clone
that need those files will fail with FileNotFoundException.
This is the #1 gotcha with shallow clones.

SECTION 6: TIME TRAVEL, CLONES, AND RECOVERY

Q18 β€” Time Travel β€” Query any version of your table

Question: "How does time travel work? Show me all the ways to use it."

sql
-- METHOD 1: Query by version number
SELECT * FROM orders VERSION AS OF 5;
SELECT * FROM orders@v5;                   -- Shorthand

-- METHOD 2: Query by timestamp
SELECT * FROM orders TIMESTAMP AS OF '2026-03-15 10:30:00';

-- See full history of changes
DESCRIBE HISTORY orders;
-- Shows: version, timestamp, operation, user, metrics

-- RESTORE: Roll back the entire table to a previous version
RESTORE TABLE orders TO VERSION AS OF 5;
-- Creates a NEW version (non-destructive) β€” you can undo the restore too!

-- Compare two versions
SELECT * FROM orders@v10 EXCEPT ALL SELECT * FROM orders@v5;
-- Shows rows that exist in v10 but not v5

Limits:

  • Data retention: 7 days default (VACUUM deletes files older than this)
  • Log retention: 30 days default (DESCRIBE HISTORY works for 30 days)
  • After VACUUM, time travel is broken for vacuumed versions

Interview Tip: "Time travel works because Delta keeps old Parquet files around. VACUUM is what deletes them. So time travel range = VACUUM retention period."

Q20 β€” Deep Clone vs Shallow Clone

Question: "What are clones? When would you use deep vs shallow?"

AspectDeep CloneShallow Clone
Copies data?YES β€” full independent copyNO β€” references source files
Independent?Fully independentDepends on source files
SpeedSlow (copies all data)Fast (copies only metadata)
Use caseProduction backups, migrationTesting, experimentation
VACUUM safe?Yes β€” owns its dataNO β€” vacuuming source breaks clone
Storage cost2x (full copy)Minimal (metadata only)
sql
-- Deep clone: Full independent copy
CREATE TABLE orders_backup DEEP CLONE orders;
-- Subsequent runs are INCREMENTAL (only copies changes since last clone)

-- Shallow clone: Fast reference copy
CREATE TABLE orders_test SHALLOW CLONE orders;
-- Perfect for testing β€” make changes without affecting production
-- WARNING: If source is VACUUMed, shallow clone may break!

SECTION 7: SPECIAL FEATURES

Answer First: "CDF is essential for incremental ETL in medallion architecture. Instead of reprocessing all Silver data to update Gold, we read only the changes. This reduces pipeline runtime from hours to minutes."

Memory Map: Change Data Feed (CDF) β€” Track row-level changes -> table property enables change recording -> each commit publishes typed row changes -> consumer reads a bounded version range -> checkpoint records downstream progress [Delta_01_Complete_Guide.md:1038].

Q21

Question: "How do you track what rows changed in a Delta table? How does downstream consume only the changes?"

sql
-- Step 1: Enable Change Data Feed on the table
ALTER TABLE orders SET TBLPROPERTIES ('delta.enableChangeDataFeed' = 'true');

-- Step 2: Read only the changes between two versions
SELECT * FROM table_changes('orders', 5, 10);
-- Returns ALL changes between version 5 and 10 with metadata:

What CDF returns:

+----------+--------+--------+------------------+----------------+
| order_id | amount | status | _change_type | _commit_version|
+----------+--------+--------+------------------+----------------+
| 1001 | 99.99 | SHIPPED| update_postimage | 7 |
| 1001 | 89.99 | PENDING| update_preimage | 7 |
| 1002 | 149.00 | NEW | insert | 8 |
| 1003 | 25.00 | CANCEL | delete | 9 |
+----------+--------+--------+------------------+----------------+
Change TypeMeaning
insertNew row was added
update_preimageRow BEFORE the update (old values)
update_postimageRow AFTER the update (new values)
deleteRow was deleted

Why CDF matters for data engineering:

WITHOUT CDF
Silver→Gold: Read ALL 10 million Silver rows, compare, find changes
Time: 45 minutes
WITH CDF
Silver→Gold: Read only 5,000 changed rows since last run
Time: 30 seconds

PySpark to read changes:

python β€” editable
# Read changes since version 5
changes_df = spark.read.format("delta") \
    .option("readChangeFeed", "true") \
    .option("startingVersion", 5) \
    .table("orders")

# Read changes since a timestamp
changes_df = spark.read.format("delta") \
    .option("readChangeFeed", "true") \
    .option("startingTimestamp", "2026-03-15") \
    .table("orders")

Interview Tip: "CDF is essential for incremental ETL in medallion architecture. Instead of reprocessing all Silver data to update Gold, we read only the changes. This reduces pipeline runtime from hours to minutes."

Answer First: Deletion vectors record removed row positions without immediately rewriting the containing Parquet file, reducing write amplification for selective updates and deletes. Readers apply the vector until later maintenance rewrites the file.

Memory Map: Deletion Vectors β€” Faster DELETEs and UPDATEs -> sparse mutation records removed row positions -> active file stays in the snapshot -> scan filters those positions at read time -> optimization rewrites data when beneficial [Delta_01_Complete_Guide.md:1101].

Q22 β€” Deletion Vectors β€” Faster DELETEs and UPDATEs

Question: "How do Deletion Vectors improve UPDATE/DELETE performance?"

Without Deletion Vectors:

πŸ“‹ Overview
UPDATE orders SET status = 'SHIPPED' WHERE order_id = 42;
Step 1: Find the 1 GB file containing order_id = 42
Step 2: Read ALL rows from that file (1 million rows)
Step 3: Change 1 row
Step 4: Write a NEW 1 GB file with the change
Step 5: Mark old file as "removed" in transaction log
Total I/O: Read 1 GB + Write 1 GB = 2 GB of I/O for ONE row change!

With Deletion Vectors:

πŸ“‹ Overview
UPDATE orders SET status = 'SHIPPED' WHERE order_id = 42;
Step 1: Find the file containing order_id = 42
Step 2: Write a tiny Deletion Vector file (~bytes) saying "row #47 in file X is deleted"
Step 3: Write a tiny new file with just the updated row
Total I/O: ~KB instead of 2 GB!
The original file is untouched β€” cleanup happens during OPTIMIZE later
sql
-- Enable deletion vectors
ALTER TABLE orders SET TBLPROPERTIES (
    'delta.enableDeletionVectors' = 'true'
);

Trade-offs:

AspectWithout DVWith DV
Write speed (UPDATE/DELETE)Slow (full file rewrite)Fast (tiny marker file)
Read speedNormalSlightly slower (must check DV files)
StorageCleanAccumulates DV files until OPTIMIZE
Best forSmall tablesLarge tables with frequent updates

Interview Tip: "Deletion Vectors change the write pattern from copy-on-write to a more efficient mark-and-sweep approach. The actual file rewrite is deferred to OPTIMIZE, which runs during off-peak hours."

Answer First: No data copying β€” recipient reads directly from your storage.

Memory Map: Delta Sharing β€” Share data without copying -> provider grants governed assets without copying files -> recipient uses a credentialed sharing client -> revocation remains under provider control -> audit events record consumption [Delta_01_Complete_Guide.md:1150].

Q23 β€” Delta Sharing β€” Share data without copying

Question: "How can you share Delta tables with external organizations without copying data?"

How it works:

πŸ“ Architecture Diagram
Provider (your org)                     Recipient (partner org)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Delta Table on  β”‚                    β”‚ Any client:     β”‚
β”‚ your storage    │───── REST API ────→│ - Spark         β”‚
β”‚ (S3/ADLS/GCS)   β”‚   (open protocol) β”‚ - pandas        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β”‚ - Power BI      β”‚
       ↑                               β”‚ - Databricks    β”‚
  Data stays HERE                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  (never copied)                        Reads directly from
                                        your storage
python β€” editable
# Recipient reads shared data β€” one line!
df = spark.read.format("deltaSharing").load("profile.json#share.schema.table")

Key benefits:

  • No data copying β€” recipient reads directly from your storage
  • Open protocol β€” not locked to Databricks (works with any client)
  • Provider controls access β€” revoke anytime, audit all reads
  • Live data β€” recipient always sees the latest version

Answer First: Problem: Different tools expect different table formats.

Memory Map: UniForm β€” One table, all formats -> Delta snapshot remains the writable authority -> generated Iceberg or Hudi metadata exposes shared files -> foreign readers consume the compatible view -> validation protects cross-engine consistency [Delta_01_Complete_Guide.md:1181].

Q24 β€” UniForm β€” One table, all formats

Question: "What is UniForm? Why does it matter?"

Problem: Different tools expect different table formats:

  • Databricks β†’ Delta Lake
  • Snowflake β†’ Iceberg
  • Trino/Presto β†’ Hudi or Iceberg
  • BigQuery β†’ Iceberg

UniForm solution: Write once as Delta, automatically generates Iceberg and Hudi metadata.

sql
-- Enable UniForm on a table
ALTER TABLE orders SET TBLPROPERTIES (
    'delta.universalFormat.enabledFormats' = 'iceberg'
);
-- Now this Delta table can be read as an Iceberg table by Snowflake, Trino, etc.
-- Data is NOT duplicated β€” only metadata is generated in Iceberg format

Why it matters:

  • One copy of data, readable by ALL engines
  • No ETL to copy data between formats
  • Eliminates "format wars" (Delta vs Iceberg vs Hudi)

Answer First: Now you've SEEN the transaction log grow. No video needed.

Memory Map: Step 6 β€” TIME TRAVEL (go back to BEFORE the update) -> historical version precedes the update -> versionAsOf reads its active files -> comparison shows the prior row state -> current table remains unchanged [Delta_01_Complete_Guide.md:1610].

Step 6 β€” TIME TRAVEL (go back to BEFORE the update)

python β€” editable
# Read version 0 (the original, before Alice got updated)
df_before = spark.read.format("delta").option("versionAsOf", 0).load(path)
df_before.show()

# EXPECTED OUTPUT (Alice is still 100.0):
# +--------+--------+------+
# |order_id|customer|amount|
# +--------+--------+------+
# |       1|   Alice| 100.0|   ← original value!
# |       2|     Bob| 200.0|
# |       3|   Carol| 300.0|
# +--------+--------+------+

# Now read current version (version 1)
df_now = spark.read.format("delta").load(path)
df_now.show()

# EXPECTED OUTPUT:
# +--------+--------+------+
# |order_id|customer|amount|
# +--------+--------+------+
# |       1|   Alice| 150.0|   ← updated value
# |       2|     Bob| 200.0|
# |       3|   Carol| 300.0|
# +--------+--------+------+
#
# 🎯 Same table. Two different versions. This is Time Travel.

Now you've SEEN the transaction log grow. No video needed.

VISUAL ANIMATION 2 β€” Time Travel Mechanics

πŸ“ Architecture Diagram
DELTA TABLE /customers/ β€” Timeline of Operations:

v0: CREATE          v1: INSERT 100      v2: UPDATE 20      v3: DELETE 5
    β”‚                   β”‚                   β”‚                   β”‚
    β–Ό                   β–Ό                   β–Ό                   β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ empty   β”‚         β”‚ file-A  β”‚         β”‚ file-A  β”‚ removed β”‚ file-A  β”‚ removed
β”‚         β”‚    β†’    β”‚         β”‚    β†’    β”‚ file-B  β”‚         β”‚ file-B  β”‚
β”‚         β”‚         β”‚         β”‚         β”‚ file-C  β”‚ added   β”‚ file-C  β”‚
β”‚         β”‚         β”‚         β”‚         β”‚         β”‚         β”‚ file-D  β”‚ added
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 _delta_log          _delta_log          _delta_log          _delta_log
 /0.json             /0.json             /0.json             /0.json
                     /1.json             /1.json             /1.json
                                         /2.json             /2.json
                                                             /3.json

TIME TRAVEL QUERIES:

  SELECT * ... VERSION AS OF 0  β†’  reads NO files        (empty table)
  SELECT * ... VERSION AS OF 1  β†’  reads file-A          (100 rows)
  SELECT * ... VERSION AS OF 2  β†’  reads file-B, file-C  (80 old + 20 updated)
  SELECT * ... VERSION AS OF 3  β†’  reads file-B, file-C,  (75 rows after delete)
                                         file-D

🧠 KEY INSIGHT:
   Delta doesn't "roll back" anything for Time Travel.
   It just reads the OLD files that are STILL on disk
   (because VACUUM hasn't removed them yet).
   Time Travel = reading old files according to an old commit.

Databricks-routed Delta concepts: 04_ETL_Scenarios_and_Design

Answer First: CDC captures changes FROM an external database INTO Delta. CDF captures changes that happen WITHIN a Delta table and makes them available to downstream consumers. They solve different problems.

Memory Map: Delta Lake Change Data Feed (CDF)? How is it different from CDC -> source CDC reads an external database log -> table change feed exposes mutations already committed in Delta -> both carry ordered operation metadata -> origin and consumer boundary distinguish them [04_ETL_Scenarios_and_Design.md:565].

Q9: What is Delta Lake Change Data Feed (CDF)? How is it different from CDC?

Simple Explanation: CDC captures changes FROM an external database INTO Delta. CDF captures changes that happen WITHIN a Delta table and makes them available to downstream consumers. They solve different problems.

Analogy: CDC is like a news reporter covering events at City Hall (external source) and writing articles (into your newspaper). CDF is like the newspaper's own internal changelog β€” when an article gets edited, CDF tells the website team "hey, this article was updated, here is the before and after."

AspectCDC (from source DB)CDF (from Delta Lake)
SourceExternal databaseDelta Lake table
CapturesChanges at source DBChanges at Delta table
Use caseIngesting external changesPropagating Delta changes downstream
MechanismDebezium, DMS, etc.Built into Delta Lake

Enable CDF:

sql
ALTER TABLE my_table SET TBLPROPERTIES (delta.enableChangeDataFeed = true);

Read changes:

# By version range
changes = spark.read.format("delta") \
.option("readChangeFeed", "true") \
.option("startingVersion", 5) \ # ← From this version...
.option("endingVersion", 10) \ # ← ...to this version
.table("my_table")
# By timestamp range
changes = spark.read.format("delta") \
.option("readChangeFeed", "true") \
.option("startingTimestamp", "2025-01-01") \
.option("endingTimestamp", "2025-01-31") \
.table("my_table")
# Streaming (incremental) β€” most common in production
changes = spark.readStream.format("delta") \
.option("readChangeFeed", "true") \
.option("startingVersion", 5) \ # ← Start from this version, then stream new changes
.table("my_table")

CDF columns added automatically:

ColumnValues
_change_typeinsert, update_preimage, update_postimage, delete
_commit_versionDelta table version
_commit_timestampWhen the change was committed

Interview Tip: CDF is the glue between medallion layers. Say "I enable CDF on Silver so that Gold only processes the changed rows, not the entire Silver table."

What NOT to Say: Confusing CDC and CDF. They sound similar but are fundamentally different. CDC = external to Delta. CDF = within Delta.

Databricks-routed Delta concepts: 05_Performance_Tuning_and_Production

Answer First: GDPR requires that when a customer requests deletion, ALL their data must be permanently removed β€” including historical versions. Delta Lake retains history by default, so you must DELETE the data and then VACUUM to physically remove old files that contain the deleted records.

Memory Map: physical erasure request -> DELETE publishes a new logical state -> retention-safe purge removes unreferenced files -> storage inspection and audit record prove completion [05_Performance_Tuning_and_Production.md:1231].

Q20: How do you handle GDPR "right to be forgotten" in Delta Lake?

Simple Explanation: GDPR requires that when a customer requests deletion, ALL their data must be permanently removed β€” including historical versions. Delta Lake retains history by default, so you must DELETE the data and then VACUUM to physically remove old files that contain the deleted records.

Analogy: Imagine writing someone's name in a notebook with carbon copies. Deleting the original page (DELETE) is not enough β€” you must also destroy all the carbon copies (VACUUM). Only then is the data truly gone.

Technical depth:

python β€” editable
# Challenge: Delta Lake retains history. GDPR requires permanent deletion.

# Step 1: Delete the customer's data from ALL tables
customer_id_to_delete = "CUST-12345"

# Delete from all relevant tables (must cover EVERY table with this customer's data)
spark.sql(f"DELETE FROM dim_customer WHERE customer_id = '{customer_id_to_delete}'")
spark.sql(f"DELETE FROM fact_orders WHERE customer_id = '{customer_id_to_delete}'")
spark.sql(f"DELETE FROM silver_interactions WHERE customer_id = '{customer_id_to_delete}'")

# Step 2: VACUUM to physically remove old files (including pre-delete versions)
# Must set retention to 0 for GDPR compliance
spark.conf.set("spark.databricks.delta.retentionDurationCheck.enabled", "false")  # ← Disable safety check
spark.sql("VACUUM dim_customer RETAIN 0 HOURS")        # ← Delete ALL old files immediately
spark.sql("VACUUM fact_orders RETAIN 0 HOURS")
spark.sql("VACUUM silver_interactions RETAIN 0 HOURS")

# Step 3: Log the deletion for compliance (prove you did it)
spark.sql(f"""
    INSERT INTO gdpr_deletion_log VALUES
    ('{customer_id_to_delete}', current_timestamp(), 'completed',
     'tables: dim_customer, fact_orders, silver_interactions')
""")

# Alternative: Use pseudonymization instead of deletion
# Replace PII with hashed/random values β€” preserves analytics while removing identity
# This is often preferred because it doesn't break aggregate reports

Interview Tip: Mention the tension: "VACUUM with 0 hours breaks time travel and concurrent readers. In practice, I batch GDPR deletions monthly and run VACUUM during a maintenance window to minimize impact on production queries."

What NOT to Say: "Just DELETE the records." Without VACUUM, the old Parquet files still contain the customer's data β€” DELETE only updates the transaction log.

Databricks-routed Delta concepts: DB_02_ETL_Pipelines_Databricks

Answer First: People often confuse CDC and CDF. They sound similar but are different things.

Memory Map: Change Data Feed (CDF)? How is it different from CDC -> database capture transports upstream inserts updates and deletes -> table-native feed publishes internal commit changes -> downstream checkpoint consumes version ranges -> architecture chooses based on mutation origin [DB_02_ETL_Pipelines_Databricks.md:421].

Q8: What is Change Data Feed (CDF)? How is it different from CDC?

Simple Explanation: People often confuse CDC and CDF. They sound similar but are different things:

  • CDC = Capturing changes from an external source system (Oracle β†’ Kafka β†’ Delta)
  • CDF = Tracking changes that happen inside a Delta table (Delta table feature)

CDF (Change Data Feed) is a Delta Lake feature. When enabled, Delta records what changed in a table β€” every insert, update, delete β€” so downstream consumers can read ONLY the changes instead of the full table.

Real-world analogy:

  • CDC = A reporter covering live events at the airport (captures external events)
  • CDF = The airport's flight status board history (records what changed inside the airport system)

Why is CDF useful? Without CDF: Gold layer reads the ENTIRE Silver table every day (slow, wasteful). With CDF: Gold layer reads only the rows that changed since last run (fast, efficient).

sql
-- Step 1: Enable CDF on a table
ALTER TABLE bookings SET TBLPROPERTIES ('delta.enableChangeDataFeed' = 'true');
-- After this, Delta starts tracking every change to this table

-- Step 2: Read changes between versions
SELECT * FROM table_changes('bookings', 5, 10);
-- Returns ONLY the rows that changed between version 5 and version 10
-- Much faster than reading the entire table!

-- Step 3: Read changes by timestamp
SELECT * FROM table_changes('bookings', '2026-03-01', '2026-03-15');
-- What changed in the bookings table during March 1-15?

-- Step 4: Streaming read of changes (for real-time downstream processing)
spark.readStream.format("delta") \
    .option("readChangeFeed", "true") \         -- Enable CDF reading
    .option("startingVersion", 5) \              -- Start from version 5
    .table("bookings")
-- This continuously reads new changes as they happen

CDF adds these extra columns to the output:

ColumnWhat It Tells YouExample Values
_change_typeWhat kind of changeinsert, update_preimage (old value), update_postimage (new value), delete
_commit_versionWhich Delta version6, 7, 8...
_commit_timestampWhen the change happened2026-03-15 10:30:00

Use cases:

  • Silver β†’ Gold incremental: Gold layer reads only changed Silver rows (not the full table)
  • Audit trail: "What changes were made to this booking last week?"
  • Downstream sync: Push changes to another system (e.g., sync to a search index)

Databricks-routed Delta concepts: DB_02_Quick_Recall

⚠️ Q21What is CDF (Change Data Feed)? How is it different from CDC?

CDC = capture changes FROM external source (Oracle β†’ Delta) CDF = track changes WITHIN Delta Lake (Delta β†’ downstream) CDF is a Delta Lake feature that records which rows changed (insert/update/delete) in a Delta table. Downstream consumers can read ONLY the changes instead of the full table.

sql
-- Enable CDF on a table
ALTER TABLE bookings SET TBLPROPERTIES ('delta.enableChangeDataFeed' = true);
-- Read only changes since version 5
SELECT * FROM table_changes('bookings', 5);

Q22What columns does CDF add?

3 columns automatically added:

  • _change_type β€” "insert", "update_preimage", "update_postimage", "delete"
  • _commit_version β€” which Delta version
  • _commit_timestamp β€” when the change happened

Databricks-routed Delta concepts: DB_03_Azure_Platform_Governance

Answer First: GDPR (EU data protection law) gives every person the right to say: "Delete ALL my data from your systems." For a travel platform, this means: when a passenger requests deletion, we must remove their data from EVERY table β€” and prove it's really gone.

Memory Map: governed erasure workflow -> tags and lineage enumerate subject data -> accountable owners delete tables, exports, and backups -> request log records regulator-facing evidence [DB_03_Azure_Platform_Governance.md:606].

Q14: How do you handle GDPR "Right to Be Forgotten" in Delta Lake?

Simple Explanation: GDPR (EU data protection law) gives every person the right to say: "Delete ALL my data from your systems." For a travel platform, this means: when a passenger requests deletion, we must remove their data from EVERY table β€” and prove it's really gone.

The challenge with Delta Lake: Delta keeps history (time travel). Even after you DELETE a row, the old data is still in the old Parquet files. You must also VACUUM to physically remove those old files.

Two approaches:

Approach 1: Hard Delete (complete removal)

sql
-- Step 1: Delete the passenger from ALL tables that contain their data
DELETE FROM dim_passenger WHERE passenger_id = 'PAX-12345';
-- Removes this passenger's row from the dimension table

DELETE FROM fact_bookings WHERE passenger_id = 'PAX-12345';
-- Removes all their bookings

DELETE FROM silver_interactions WHERE passenger_id = 'PAX-12345';
-- Removes all their interaction records

-- Step 2: VACUUM with 0 hours retention to physically remove old files
-- WARNING: This breaks time travel! But GDPR requires it.
SET spark.databricks.delta.retentionDurationCheck.enabled = false;
-- Must disable the safety check (Delta normally prevents 0-hour vacuum)

VACUUM dim_passenger RETAIN 0 HOURS;
-- Physically deletes ALL old files β€” data is truly gone
VACUUM fact_bookings RETAIN 0 HOURS;
VACUUM silver_interactions RETAIN 0 HOURS;

-- Step 3: Log the deletion for compliance audit
INSERT INTO gdpr_deletion_log VALUES (
    'PAX-12345',                     -- Which passenger was deleted
    current_timestamp(),             -- When it was deleted
    'completed',                     -- Status
    'tables: dim_passenger, fact_bookings, silver_interactions'  -- Which tables
);
-- This log PROVES to regulators that we deleted the data

Approach 2: Pseudonymization (preferred β€” keeps analytics working) Instead of deleting the entire row, replace PII with random/hashed values. The row stays for analytics, but the person can't be identified.

sql
UPDATE dim_passenger SET
    full_name = 'REDACTED',                           -- Can't identify the person
    email = CONCAT(MD5(email), '@redacted.com'),      -- Hashed (one-way, can't reverse)
    phone = 'REDACTED',
    address = 'REDACTED'
WHERE passenger_id = 'PAX-12345';
-- The booking data still exists for analytics (revenue, route stats)
-- But the person's identity is completely removed

Interview tip: Mention BOTH approaches and when to use each. Hard delete = when regulation requires complete removal. Pseudonymization = when you need to keep analytics data but remove identity.

Databricks-routed Delta concepts: DB_03_Quick_Recall

Q28How do you implement Right to be Forgotten in Delta Lake?

  1. DELETE the user's rows from ALL tables
  2. Run VACUUM table RETAIN 0 HOURS to physically remove old files
  3. ⚠️ This breaks time travel β€” old versions with that user's data are gone

Routed Delta question-bank prompts β€” 30-time-travel-cdf

8. What is Time Travel in Delta Lake? How do you query an older version?

Querying a previous version of a Delta table. Use SELECT * FROM table VERSION AS OF 5 or SELECT * FROM table TIMESTAMP AS OF '2026-03-15'. Works because the transaction log retains history of all file additions/removals.

9. What is the VACUUM command and what does it do?

Physically deletes old Parquet files that are no longer referenced by the current table version. Frees storage but removes the ability to time travel to versions that used those files.

10. What is the default retention period for VACUUM? > 7 days (168 hours). Files newer than 7 days are never deleted, ensuring active queries can complete. Override with VACUUM table RETAIN X HOURS.

16. What is Change Data Feed (CDF) in Delta Lake? > A feature that records row-level changes (insert, update_preimage, update_postimage, delete) for each commit. Downstream consumers read ONLY the changes instead of reprocessing the entire table. Enable with delta.enableChangeDataFeed = true.

20. What is the RESTORE command in Delta Lake? > Rolls back a Delta table to a previous version: RESTORE TABLE table TO VERSION AS OF 5. Creates a new commit that makes the table state identical to the specified version. Used for recovering from bad writes.

9. What happens if you run VACUUM with a retention of 0 hours? What are the risks?

Key points: Deletes ALL files not in the current version, even files being actively read by queries. Risks: (1) active queries fail with FileNotFoundException, (2) Time Travel breaks for all previous versions, (3) concurrent writers may reference files that get deleted. Use 0 hours only for GDPR deletion where you must physically remove data. Set spark.databricks.delta.retentionDurationCheck.enabled = false to allow it.

15. Explain how Time Travel works internally β€” what is stored in each JSON commit file? > Key points: Each JSON commit file records: (1) add actions β€” new Parquet files added, (2) remove actions β€” files logically deleted, (3) metadata changes (schema, properties), (4) operation type and metrics. To read version N, Delta replays all commits from the last checkpoint up to N, building the set of active files. Old files remain on storage until VACUUM deletes them.

16. Compare Change Data Feed (CDF) vs reading the transaction log directly for CDC. > Key points: CDF provides a clean API: spark.read.option("readChangeFeed", "true").table() returning rows with _change_type (insert, update_preimage, update_postimage, delete). Reading the log directly requires parsing JSON files and reconstructing changes yourself β€” complex, error-prone, and unsupported. CDF is the right approach; it's purpose-built, handles edge cases, and provides pre/post images for updates.

15. What is the DESCRIBE HISTORY command used for? > Shows the commit history of a Delta table β€” who made each change, when, what operation (WRITE, MERGE, OPTIMIZE), and the version number. Used for auditing and time travel reference.

5. What is the difference between OPTIMIZE and VACUUM? Can you run them together?

Key points: OPTIMIZE compacts small files into larger ones (creates new files, doesn't delete old ones). VACUUM deletes old, unreferenced files. They're complementary: OPTIMIZE first (creates new compacted files), then VACUUM (cleans up the old small files). Running in sequence is safe; order matters β€” OPTIMIZE before VACUUM.

Advanced

Delta Performance and Maintenance

#

Delta Performance and Maintenance

Answer First: Delta maintenance balances file size, data layout, skipping statistics, write amplification, retention, and concurrent-reader safety against measured workload evidence.

Memory Map: measure -> file layout -> compact/cluster -> retain history -> vacuum safely -> remeasure.

Current liquid clustering semantics

Answer First: Liquid clustering is incremental because OPTIMIZE rewrites data files as necessary to cluster records; defining or changing clustering keys does not continuously recluster every write.

Writes and updated rows are not automatically reclustered without OPTIMIZE. Predictive optimization can schedule that work for eligible Unity Catalog managed tables, but CLUSTER BY alone is a layout declaration, not a background maintenance loop. See the official liquid clustering guide.

Answer First: If someone asks for data from March 15, you skip ALL folders that don't include March.

Memory Map: file-level statistics and data skipping -> writer records minimum maximum and null counts -> query predicate rejects impossible ranges -> reader opens only candidate files -> scanned bytes prove pruning [03_Delta_Lake_and_Lakehouse.md:122].

Q4: Explain file-level statistics and data skipping.

Simple Explanation: Imagine 100 file folders, each labeled with a date range (Jan 1-15, Jan 16-31, Feb 1-15...). If someone asks for data from March 15, you skip ALL folders that don't include March. Delta does the same β€” each Parquet file has min/max statistics, and Delta skips files that can't contain matching rows.

Answer: Delta stores min/max statistics for the first 32 columns (by default) in the transaction log.

How data skipping works:

sql
SELECT * FROM orders WHERE order_date = '2025-01-15'
  1. Delta reads file statistics from the transaction log
  2. For each file, checks: min(order_date) <= '2025-01-15' AND max(order_date) >= '2025-01-15'
  3. Files where the condition is impossible are skipped entirely
  4. Only matching files are read

Configuration: delta.dataSkippingNumIndexedCols (default 32)

Interview Tip: "Data skipping works BEST when data is sorted/clustered. That's why Z-ORDER and Liquid Clustering are so important β€” they make min/max ranges tight per file, so more files get skipped."

What NOT to Say: "Data skipping reads file headers" β€” No, stats are stored in the TRANSACTION LOG, not in the Parquet files themselves. Delta reads the log, not the data.

Answer First: OPTIMIZE rewrites and compacts active files to improve layout; VACUUM deletes unreferenced files older than the retention threshold. OPTIMIZE changes current physical organization, whereas VACUUM removes historical recovery files.

Memory Map: the difference between OPTIMIZE and VACUUM -> difference between optimize and vacuum sets one retention, safety, and deletion boundary -> unreferenced file ages past the policy -> safety check protects readers and history -> physical deletion removes the file -> history and storage checks confirm irreversibility [03_Delta_Lake_and_Lakehouse.md:149].

Q5: What is the difference between OPTIMIZE and VACUUM?

Simple Explanation: OPTIMIZE = Organizing your messy desk. 10,000 Post-it notes scattered everywhere β†’ combine them into 10 neat notebooks. Easier to find things now! VACUUM = Throwing away old drafts you no longer need. Once thrown away, you can't go back to them.

Answer:

AspectOPTIMIZEVACUUM
PurposeCompacts small files into larger onesPhysically deletes old unreferenced files
Performance impactYes β€” improves readsNo direct performance gain β€” frees storage
Data safetyNon-destructive (old files still exist)DESTRUCTIVE β€” files removed permanently
Default retentionN/A7 days (delta.deletedFileRetentionDuration)
Time travel impactNoneBreaks time travel before vacuum threshold
sql
-- Compact files (target ~1 GB per file)
OPTIMIZE orders;

-- Compact + co-locate by column
OPTIMIZE orders ZORDER BY (customer_id);

-- Clean up old files (7-day retention)
VACUUM orders;

-- Clean up with custom retention
VACUUM orders RETAIN 168 HOURS;  -- 7 days

Interview Tip: "Always mention OPTIMIZE and VACUUM together β€” they're a pair. OPTIMIZE creates new files, VACUUM cleans up the old ones. Without VACUUM after OPTIMIZE, storage keeps growing."

What NOT to Say: "OPTIMIZE deletes old files" β€” No, OPTIMIZE is non-destructive. VACUUM is the one that deletes.

Answer First: Breaks ALL time travel β€” can't query any previous version

Memory Map: Can you run VACUUM with retention of 0 hours? What are the risks -> zero-hour policy makes every unreferenced file immediately eligible -> concurrent readers may still need those files -> deletion destroys history and recovery -> retention safety check blocks the unsafe request [03_Delta_Lake_and_Lakehouse.md:186].

Q6: Can you run VACUUM with retention of 0 hours? What are the risks?

Answer: Yes, but you must disable the safety check:

sql
SET spark.databricks.delta.retentionDurationCheck.enabled = false;
VACUUM my_table RETAIN 0 HOURS;

Risks:

  1. Breaks ALL time travel β€” can't query any previous version
  2. Concurrent readers may fail β€” readers that started before VACUUM may reference deleted files
  3. No recovery β€” deleted data is gone permanently

Rule: NEVER do this in production. Use default 7-day retention.

What NOT to Say: "I'll set retention to 0 to save storage" β€” This is a red flag for interviewers. Shows you don't understand the risk to concurrent readers and time travel.

SECTION 4: Z-ORDERING, LIQUID CLUSTERING, OPTIMIZATION

Answer First: Works great for dates, bad for user id (millions of tiny drawers).

Memory Map: Z-Ordering? How does it differ from partitioning -> z ordering it differ from partitioning chooses workload-specific layout keys and rewrite behavior -> measured filters select layout keys -> OPTIMIZE rewrites files as needed -> data locality improves skipping -> files and bytes scanned validate layout [03_Delta_Lake_and_Lakehouse.md:464].

Q15: What is Z-Ordering? How does it differ from partitioning?

Simple Explanation: Partitioning = Filing cabinet with one drawer per month. Need January data? Open only the January drawer. Works great for dates, bad for user_id (millions of tiny drawers). Z-Ordering = Within each drawer, sorting papers by customer name. Now you can quickly find "Customer X" without scanning the entire drawer.

Answer:

AspectPartitioningZ-Ordering
How it worksCreates separate directories per valueCo-locates related data within files using space-filling Z-curve
CardinalityLow cardinality (< 1000 values)High cardinality (user_id, order_id)
Query patternAlmost always filter on partition columnFrequently filter on the Z-ordered column
OverheadOver-partitioning creates small filesRequires running OPTIMIZE
CombinationCan be combinedZ-ORDER should NOT use partition columns
sql
-- Partition by date (low cardinality), Z-order by customer_id (high cardinality)
CREATE TABLE orders (
    order_id LONG,
    customer_id LONG,
    order_date DATE,
    amount DECIMAL(10,2)
) PARTITIONED BY (order_date);

OPTIMIZE orders ZORDER BY (customer_id);

Z-ORDER best practices:

  • Max 4 columns (effectiveness decreases with more)
  • Choose columns in WHERE, JOIN, MERGE conditions
  • High cardinality columns benefit most
  • NOT idempotent β€” running again rewrites files

Answer First: Liquid Clustering (GA in Databricks) is the next-generation data layout that replaces both partitioning and Z-Ordering.

Memory Map: Liquid Clustering? How does it improve over Z-Ordering + Partitioning -> declared keys replace static directory partitions -> incremental rewrites cluster affected files -> key changes avoid full table recreation -> skipping metrics measure locality improvement [03_Delta_Lake_and_Lakehouse.md:499].

Q16: What is Liquid Clustering? How does it improve over Z-Ordering + Partitioning?

Answer: Liquid Clustering (GA in Databricks) is the next-generation data layout that replaces both partitioning and Z-Ordering.

AspectZ-OrderingLiquid Clustering
When appliedManual OPTIMIZE ZORDER BYAutomatic on writes (or OPTIMIZE)
IncrementalRewrites the selected Z-ORDER scopeYes (rewrites files needing clustering)
Change keysMust re-OPTIMIZE everythingJust ALTER β€” new writes use new keys
PartitioningSeparate conceptReplaces partitioning
Small filesDoesn't addressHandles automatically
sql
-- Create with liquid clustering (replaces PARTITIONED BY + ZORDER)
CREATE TABLE orders (
    order_id LONG,
    customer_id LONG,
    order_date DATE,
    amount DECIMAL(10,2)
) CLUSTER BY (customer_id, order_date);

-- Change clustering keys without rewriting data
ALTER TABLE orders CLUSTER BY (order_date, region);

-- Trigger optimization
OPTIMIZE orders;

When to use what (2026 recommendation):

  • New tables: Always use Liquid Clustering
  • Existing tables with partitioning: Migrate to Liquid Clustering when possible
  • Legacy tables: Continue with partitioning + Z-Ordering

Interview Tip: "For new tables, I always use CLUSTER BY instead of PARTITIONED BY. It's incremental, handles any cardinality, and I can change the clustering columns without rewriting the table."

What NOT to Say: "I'll partition by user_id" β€” High cardinality partitioning creates millions of tiny files. Use Liquid Clustering for high cardinality columns.

Answer First: Inspect effective properties and runtime support before changing them because some settings alter compatibility or history.

Memory Map: the most important Delta table properties -> effective properties control retention statistics and features -> protocol support determines valid values -> change may affect readers writers or maintenance -> detail and history verify the active configuration [03_Delta_Lake_and_Lakehouse.md:567].

Q18: What are the most important Delta table properties?

Answer:

sql
ALTER TABLE my_table SET TBLPROPERTIES (
    -- Write optimization
    'delta.autoOptimize.optimizeWrite' = 'true',         -- Coalesce small files on write
    'delta.autoOptimize.autoCompact' = 'true',            -- Auto compaction after writes

    -- Change Data Feed
    'delta.enableChangeDataFeed' = 'true',                -- Track row-level changes

    -- Retention
    'delta.logRetentionDuration' = 'interval 30 days',    -- How long to keep commit logs
    'delta.deletedFileRetentionDuration' = 'interval 7 days',  -- VACUUM threshold

    -- Column mapping (enables column rename/drop)
    'delta.columnMapping.mode' = 'name',

    -- Deletion vectors (faster deletes)
    'delta.enableDeletionVectors' = 'true',

    -- Checkpoint interval
    'delta.checkpointInterval' = '10'
);

Answer First: Safe VACUUM policy starts from the longest reader and writer duration, preserves required recovery history, and rejects aggressive retention as a routine optimization.

Memory Map: Scenario β€” A Delta table has 10,000 small files (each <1 MB). Queries are slow. How do you fix this -> delta table has 10 000 small files each 1 mb queries slow fix this isolates the relevant fragmentation or maintenance cause -> file-count baseline exposes fragmentation -> compaction policy selects candidate files -> rewritten layout reduces overhead -> operation metrics and scan time prove benefit [03_Delta_Lake_and_Lakehouse.md:792].

Q27: Scenario β€” A Delta table has 10,000 small files (each <1 MB). Queries are slow. How do you fix this?

Answer:

sql
-- Step 1: Compact files immediately
OPTIMIZE slow_table;                                    -- Merge into ~1 GB files
OPTIMIZE slow_table ZORDER BY (frequently_filtered_col); -- Plus data skipping

-- Step 2: Clean up old files
VACUUM slow_table RETAIN 168 HOURS;

-- Step 3: Prevent future small files
ALTER TABLE slow_table SET TBLPROPERTIES (
    'delta.autoOptimize.optimizeWrite' = 'true',     -- Coalesce on write
    'delta.autoOptimize.autoCompact' = 'true'         -- Auto compact after writes
);

-- Step 4: For streaming sources, increase trigger interval
# .trigger(processingTime="5 minutes")  -- Instead of "10 seconds"

-- Step 5: For new tables, use Liquid Clustering
-- CREATE TABLE ... CLUSTER BY (col)  -- Handles compaction automatically

Q4: What is data skipping? How do file-level statistics work?

Simple Explanation: When you query a Delta table, you don't want to read ALL the data files. That's wasteful. Delta stores min and max values for each column in each data file (inside the transaction log). When you run a query with a WHERE clause, Delta checks: "Can this file possibly contain matching rows?" If the answer is NO, it SKIPS the file entirely.

Real-world analogy: You have 100 file folders, each labeled with date ranges (Jan 1-15, Jan 16-31, Feb 1-15, ...). If someone asks for bookings on March 15, you skip ALL folders that don't include March 15. You only open the relevant folder.

sql
-- Query: Find bookings on a specific date
SELECT * FROM bookings WHERE booking_date = '2026-03-15'

-- What Delta does behind the scenes:
-- File A: min(booking_date) = 2026-01-01, max = 2026-01-31
--         β†’ SKIP! March 15 can't possibly be in this file
-- File B: min(booking_date) = 2026-03-01, max = 2026-03-31
--         β†’ READ! March 15 might be in this file
-- File C: min(booking_date) = 2026-06-01, max = 2026-06-30
--         β†’ SKIP! March 15 can't be here either

Key details:

  • Stats are stored for the first 32 columns by default
  • Config: delta.dataSkippingNumIndexedCols (default 32)
  • Works best when data is sorted/clustered (that's why OPTIMIZE + Z-ORDER helps!)
  • For a 10 TB booking table, data skipping can reduce scan from 10 TB to just 50 GB

Why this matters: With billions of booking records, scanning the entire table for one date would take ages. Data skipping makes queries fast by reading only the relevant files.

SECTION 3: OPTIMIZE, VACUUM, Z-ORDER, LIQUID CLUSTERING (1 hour)

Q9: What is OPTIMIZE? What is VACUUM? What's the difference?

Simple Explanation:

OPTIMIZE = File compaction. Over time, your table accumulates many small files (especially with streaming or frequent small writes). Small files are bad for performance because each file means a separate read operation. OPTIMIZE combines many small files into fewer large files (~1 GB each).

Real-world analogy: You have 10,000 Post-it notes scattered on your desk. OPTIMIZE = combining them into 10 neat notebooks. Much easier to find things now!

VACUUM = Garbage collection. When you UPDATE or DELETE data in Delta, the old files are NOT deleted immediately (they're kept for time travel). Over time, these old unused files pile up and waste storage. VACUUM physically deletes old files that are no longer needed.

Real-world analogy: VACUUM = throwing away old drafts that you no longer need. Once thrown away, you can't go back to them.

AspectOPTIMIZEVACUUM
What it doesCombines small files β†’ fewer large filesDeletes old unused files from storage
Why you need itSmall files β†’ slow readsOld files β†’ wasted storage cost
Is it destructive?No β€” old files still exist afterYES β€” old files are permanently deleted
Affects time travel?NoYes β€” you can't time travel to versions whose files were vacuumed
Default retentionN/A7 days (files older than 7 days get deleted)
How often to runDaily or after large writesWeekly or after OPTIMIZE
sql
-- OPTIMIZE: Combine small files into ~1 GB files
OPTIMIZE flight_schedules;
-- Example: 10,000 small files (1 MB each) β†’ 10 large files (1 GB each)
-- Result: Queries that used to take 5 minutes now take 30 seconds

-- OPTIMIZE + Z-ORDER: Combine files AND sort data by a column
-- (See Q10 for what Z-ORDER means)
OPTIMIZE flight_schedules ZORDER BY (departure_airport, flight_date);

-- VACUUM: Delete old files that are older than 7 days (default)
VACUUM flight_schedules;
-- This frees up storage space on ADLS Gen2

-- You can specify custom retention period:
VACUUM flight_schedules RETAIN 168 HOURS;  -- 168 hours = 7 days

-- DANGEROUS β€” never do this in production:
-- VACUUM flight_schedules RETAIN 0 HOURS;
-- This deletes ALL old files immediately, breaking ALL time travel!

Interview tip: Always mention OPTIMIZE and VACUUM together β€” they're a pair. OPTIMIZE creates new files, VACUUM cleans up the old ones.

Answer First: These are 3 different ways to organize your data for faster queries. Let's understand each one.

Memory Map: Z-Ordering? What is Partitioning? What is Liquid Clustering -> directory partitioning filters coarse values -> multidimensional ordering colocates related ranges -> adaptive clustering manages evolving keys -> cardinality and filter patterns choose the layout [DB_01_Delta_Lake_Deep_Dive.md:369].

Q10: What is Z-Ordering? What is Partitioning? What is Liquid Clustering?

These are 3 different ways to organize your data for faster queries. Let's understand each one:

PARTITIONING β€” The oldest and simplest approach

Simple Explanation: Partitioning creates separate folders for each value of a column. If you partition by booking_date, each date gets its own folder. When you query WHERE booking_date = '2026-03-15', Spark only reads the March 15 folder.

Real-world analogy: Filing cabinet with one drawer per month. Need January data? Open only the January drawer.

Problem: Only works for low-cardinality columns (few unique values). If you partition by passenger_id (millions of unique values), you get millions of tiny folders = disaster (called "over-partitioning").

sql
-- Good: booking_date has ~365 values per year β†’ manageable
CREATE TABLE bookings (...) PARTITIONED BY (booking_date);

-- BAD: passenger_id has millions of values β†’ millions of tiny folders!
-- CREATE TABLE bookings (...) PARTITIONED BY (passenger_id);  -- DON'T DO THIS

Z-ORDERING β€” Sort data WITHIN files for better data skipping

Simple Explanation: Z-ORDER doesn't create separate folders. Instead, it sorts and groups related data WITHIN the Parquet files so that similar values are close together. This makes data skipping (Q4) much more effective.

Real-world analogy: Imagine a library. Partitioning = separate rooms per genre. Z-ORDER = within each room, books are sorted by author name. If you want "books by author X", you go to a specific shelf, not search the entire room.

Why "Z-ORDER"? It uses a mathematical technique called "Z-curve" (space-filling curve) to sort data on multiple columns simultaneously. You don't need to understand the math β€” just know it groups similar values together.

sql
-- Z-ORDER is always used WITH OPTIMIZE (not standalone)
OPTIMIZE bookings ZORDER BY (passenger_id);
-- This reorganizes all files so that passenger_id values are grouped together
-- Now queries like WHERE passenger_id = 'PAX-123' can skip most files

-- You can Z-ORDER on up to 4 columns (more than 4 = less effective)
OPTIMIZE bookings ZORDER BY (departure_airport, booking_date);

Limitations of Z-ORDER:

  • Must run manually (OPTIMIZE ZORDER BY ...)
  • Rewrites ALL files every time (slow for large tables)
  • Can't change Z-ORDER columns easily

LIQUID CLUSTERING β€” The NEW and BEST approach (2024+)

Simple Explanation: Liquid Clustering replaces partitioning and Z-ordering for supported tables. OPTIMIZE incrementally selects and rewrites files that need clustering, and clustering keys can change without an immediate full-table rewrite.

Think of it as "smart auto-organizing" β€” Delta figures out the best way to arrange your data based on the columns you specify.

sql
-- Create table with Liquid Clustering (replaces PARTITIONED BY + ZORDER)
CREATE TABLE bookings (
    booking_id LONG,
    passenger_id LONG,
    booking_date DATE,
    fare_amount DECIMAL(10,2)
) CLUSTER BY (booking_date, passenger_id);  -- ← Use CLUSTER BY instead of PARTITIONED BY
-- The clustering keys guide subsequent OPTIMIZE work

-- Change clustering columns anytime β€” NO full rewrite needed!
ALTER TABLE bookings CLUSTER BY (departure_airport, booking_date);
-- Existing data is not rewritten by ALTER TABLE alone.

-- Trigger incremental clustering; OPTIMIZE rewrites files as necessary.
OPTIMIZE bookings;
-- The selected rewrite set depends on which files still need clustering.

Comparison summary:

AspectPartitioningZ-OrderingLiquid Clustering
What it doesSeparate folders per valueSorts data within filesAuto-organizes data
Good forLow cardinality (date, country)High cardinality (user_id)Any cardinality
Applied whenOn writeManual OPTIMIZE commandDuring OPTIMIZE or predictive optimization
IncrementalN/ARewrites the specified Z-ORDER scopeRewrites files that need clustering
Change columnsRequires full rewriteMust re-OPTIMIZE everythingJust ALTER TABLE
Replaces others?β€”NoYES β€” replaces both

Recommendation for a travel platform:

  • New tables: Always use Liquid Clustering
  • Existing partitioned tables: Migrate to Liquid Clustering when possible
  • Z-ORDER tips: If you're on older tables, max 4 columns, choose columns used in WHERE/JOIN/MERGE

Interview tip: If they ask "How would you organize a new bookings table?", answer: "I'd use Liquid Clustering with CLUSTER BY (booking_date, departure_airport) because it's incremental, automatic, and I can change the keys later without rewriting data."

Q12: What are the important Delta table properties?

Simple Explanation: Delta tables have settings (called "table properties") that control behavior β€” like auto-compaction, change tracking, retention periods, etc. These are set using ALTER TABLE ... SET TBLPROPERTIES.

sql
ALTER TABLE bookings SET TBLPROPERTIES (

    -- AUTO OPTIMIZATION: Automatically fix small files problem
    'delta.autoOptimize.optimizeWrite' = 'true',
    -- What: When writing data, Delta automatically combines small output files
    -- Why: Prevents the small file problem without manually running OPTIMIZE

    'delta.autoOptimize.autoCompact' = 'true',
    -- What: After each write, Delta automatically runs a mini-OPTIMIZE
    -- Why: Keeps files at a healthy size over time

    -- CHANGE DATA FEED (CDF): Track what changed row-by-row
    'delta.enableChangeDataFeed' = 'true',
    -- What: Records every INSERT/UPDATE/DELETE at the row level
    -- Why: Downstream tables can read only the changes (not the full table)
    -- Example: Gold layer reads only changed Silver rows β†’ faster pipeline

    -- RETENTION: How long to keep old data for time travel
    'delta.logRetentionDuration' = 'interval 30 days',
    -- What: Keep commit logs for 30 days (for DESCRIBE HISTORY)

    'delta.deletedFileRetentionDuration' = 'interval 7 days',
    -- What: VACUUM won't delete files newer than 7 days
    -- Why: Protects running queries and time travel for 7 days

    -- COLUMN MAPPING: Enable column rename and drop
    'delta.columnMapping.mode' = 'name',
    -- What: Maps columns by name instead of position
    -- Why: Allows ALTER TABLE RENAME COLUMN and DROP COLUMN
    -- Without this: you can't rename or drop columns in Delta

    -- DELETION VECTORS: Faster deletes/updates
    'delta.enableDeletionVectors' = 'true'
    -- What: Mark rows as deleted without rewriting files (see Q11)
);

Interview tip: Know the top 3: autoOptimize, enableChangeDataFeed, and columnMapping.mode. These are the most commonly discussed in interviews.

Answer First: Predictive Optimization observes managed-table usage and automatically schedules maintenance such as OPTIMIZE, VACUUM, and statistics collection. Databricks owns the scheduling while operators monitor maintenance history and cost.

Memory Map: Predictive Optimization -> usage telemetry identifies maintenance need -> service schedules compaction cleanup and statistics -> eligible managed tables receive operations automatically -> history and billing reveal effect [DB_01_Delta_Lake_Deep_Dive.md:673].

Q18: What is Predictive Optimization?

Simple Explanation: Remember how we said you need to manually run OPTIMIZE, VACUUM, and ANALYZE TABLE to keep your tables healthy? Predictive Optimization does this AUTOMATICALLY. Databricks watches how your tables are used and runs these commands at the right time, without you scheduling anything.

Real-world analogy: Like a self-cleaning oven. Instead of manually scheduling "clean the oven every Sunday," the oven detects when it's dirty and cleans itself.

Key points:

  • Automatically runs OPTIMIZE (file compaction)
  • Automatically runs VACUUM (cleanup old files)
  • Automatically runs ANALYZE TABLE (refresh statistics)
  • Enabled by default on all new Unity Catalog managed tables (since 2025)
  • Learns your table's access patterns to optimize scheduling
  • No configuration needed β€” just use managed tables!

Example answer: "For our 500+ Delta tables, Predictive Optimization eliminates the need for manual OPTIMIZE/VACUUM scheduling β€” the platform learns each table's access patterns and optimizes automatically. This saves our team hours of maintenance work."

SECTION 3: OPTIMIZE, VACUUM, Z-ORDER, LIQUID CLUSTERING

🧠 Memory Map: File Performance

🧠 OPTIMIZE = "Combines small files into big files"
THE SMALL FILE PROBLEM
1000 tiny files (1 MB each) = SLOW to read (too many file opens)
1 huge file (1 TB) = SLOW to read (can't parallelize)
Sweet spot = files between 128 MB-1 GB
OPTIMIZE"Combines small files into big files"
OPTIMIZE bookings; -- Compacts ALL small files
Result: 1000 tiny files→10 large files
VACUUM"Deletes old unused files from disk"
VACUUM bookings RETAIN 168 HOURS; -- Delete files older than 7 days
Default retention: 7 days (168 hours)
⚠️VACUUM < 7 days = BREAKS time travel!
Z-ORDER = "Sorts data by columns for fast lookups" (OLD way)
OPTIMIZE bookings ZORDER BY (airport_code, booking_date);
Good for: 1-4 columns that you filter on often
LIQUID CLUSTERING"Smart Z-ORDER that auto-maintains" (NEW way β€” 2024+)
CREATE TABLE bookings CLUSTER BY (airport_code, booking_date);
⚠️REPLACES both partitioning AND Z-ORDER
⚠️Cannot use Liquid Clustering WITH partitioning or Z-ORDER
Remember: "OV-ZL" = Optimize, Vacuum, Z-Order→Liquid (old→new)

SECTION 1: OPTIMIZE -- When It Helps and When It Hurts

Answer First: OPTIMIZE selects eligible active files, rewrites their rows into a better-sized or clustered layout, and commits add/remove actions atomically. It is idempotent for files that already satisfy the requested layout.

Memory Map: What exactly does OPTIMIZE do internally? Walk me through the mechanics -> active small files become rewrite candidates -> rows are read and repacked into larger files -> one commit swaps old actions for new actions -> file counts and sizes expose the result [DB_06_Delta_Lake_Advanced_Masterclass.md:26].

Q1: What exactly does OPTIMIZE do internally? Walk me through the mechanics.

Answer:

OPTIMIZE is a table maintenance command that compacts small files into larger, optimally-sized files. But understanding the mechanics is what separates a good answer from a great one.

Step-by-step internal process:

πŸ“‹ Overview
OPTIMIZE my_table
Step 1: Read the transaction log to get the current list of active files
Step 2: Identify "small" files (below the target size threshold)
Default target: 1 GB per file (configurable via spark.databricks.delta.optimize.maxFileSize)
Step 3: Group small files by partition (if partitioned)
Step 4: For each group:
a. Read all small files into memory
b. Rewrite them into fewer, larger files (target ~1 GB each)
c. Write new Parquet files to storage
Step 5: Create a new transaction log entry:
"remove" actions for all old small files
"add" actions for the new compacted files
Step 6: Old files are NOT physically deleted yet -- they remain until VACUUM runs

Key detail interviewers probe on: OPTIMIZE is an idempotent operation. Running it twice in a row on an already-optimized table is a no-op (it sees no small files to compact). The second run reads the log, finds no files below threshold, and does nothing.

What OPTIMIZE does NOT do:

  • Does NOT delete old files (that is VACUUM's job)
  • Does NOT change data content -- only file layout
  • Does NOT update table statistics by itself (though rewriting files refreshes file-level stats)
  • Does NOT reorder data unless you add ZORDER BY or use Liquid Clustering

Answer First: Compute cost: Reading all small files + writing new large files (write amplification)

Memory Map: you NOT run OPTIMIZE? Give me real scenarios where it causes problems -> already healthy files offer little scan benefit -> heavy rewrites consume compute and storage I O -> concurrent write patterns can cause conflict -> measured fragmentation must justify execution [DB_06_Delta_Lake_Advanced_Masterclass.md:61].

Q2: When should you NOT run OPTIMIZE? Give me real scenarios where it causes problems.

Answer -- this is the trap question most candidates fail:

Scenario 1: During active write windows

Pipeline A: Writing streaming micro-batches every 30 seconds
OPTIMIZE: Running at the same time
Problem: OPTIMIZE rewrites files that Pipeline A is also modifying.
Result: Increased write amplification. OPTIMIZE reads+rewrites files,
then Pipeline A's next commit conflicts with OPTIMIZE's commit.
Optimistic concurrency handles it, but you get retries, wasted I/O,
and slower pipeline throughput.
Fix: Schedule OPTIMIZE during off-peak hours (e.g., 2 AM nightly).

Scenario 2: Append-only tables with time-partitioning

Table: event_log, partitioned by date
Usage: Only queries on recent data. Old partitions are never queried.
Problem: Running "OPTIMIZE event_log" with no WHERE clause
optimizes ALL partitions, including 3-year-old data nobody queries.
Fix: Always use a predicate:
OPTIMIZE event_log WHERE event_date >= current_date() - INTERVAL 7 DAYS

Scenario 3: Liquid Clustering tables

If a table uses Liquid Clustering, OPTIMIZE triggers the clustering algorithm
automatically. You do NOT add ZORDER BY -- that syntax is incompatible
with Liquid Clustering tables.
Wrong: OPTIMIZE my_lc_table ZORDER BY (col1) -- ERROR!
Right: OPTIMIZE my_lc_table -- Clustering happens automatically

Scenario 4: Tables with Deletion Vectors enabled

Tables using deletion vectors have "soft deletes" -- the data files
are marked with deletion vectors but not rewritten.
Running OPTIMIZE on these tables will:
1. Compact small files (normal behavior)
2. Also purge deletion vectors by rewriting affected files
This is actually GOOD for read performance but costs extra compute.
Be aware of this dual behavior when sizing your OPTIMIZE jobs.

Scenario 5: Very large tables with limited cluster resources

Table: 50 TB, 500,000 files
Cluster: 8 nodes, 64 GB RAM each
OPTIMIZE tries to rewrite everything -- runs for 6+ hours,
may OOM or hit spot instance termination.
Fix: Use WHERE to target specific partitions, or use
Predictive Optimization (Section 6) to let Databricks handle it.

Common follow-up: "What is the cost of OPTIMIZE?"

  • Compute cost: Reading all small files + writing new large files (write amplification)
  • Storage cost: Old files remain until VACUUM -- you temporarily have 2x storage
  • I/O cost: Proportional to the amount of data being rewritten

Answer First: Use only a small number of Z-ORDER columns that repeatedly appear in selective filters; adding columns dilutes locality and raises rewrite cost. Workload measurements, not a universal count, determine the choice.

Memory Map: the difference between bin-packing OPTIMIZE and OPTIMIZE with ZORDER -> bin packing targets file size only -> multidimensional ordering also colocates selected values -> additional sorting increases rewrite cost -> filter workload determines whether locality pays [DB_06_Delta_Lake_Advanced_Masterclass.md:133].

Q3: What is the difference between bin-packing OPTIMIZE and OPTIMIZE with ZORDER?

Answer:

Plain OPTIMIZE (bin-packing only):
Goal: Reduce file count by combining small files into ~1 GB files
Data ordering: Data within the new files is in the order it was originally written
Use case: You just want fewer files for faster listing and fewer tasks
Speed: Fast -- just concatenates files
OPTIMIZE ... ZORDER BY (col1, col2):
Goal: Reduce file count AND co-locate related data within each file
Data ordering: Data is rearranged using a space-filling Z-order curve
Use case: You want data skipping to work better for multi-column filter queries
Speed: Slower -- requires sorting/rearranging data, not just concatenation
Cost: 2-5x more expensive than plain OPTIMIZE because of the sorting step

Why Z-ORDER is more expensive:

Plain OPTIMIZE: ZORDER OPTIMIZE:
Read File A (10 MB) Read File A (10 MB)
Read File B (10 MB) Read File B (10 MB)
Read File C (10 MB) Read File C (10 MB)
Concatenate A+B+C -> File D (30 MB) Sort all rows by Z-order curve on (col1, col2)
Write File D Write File D (same 30 MB but rows are reordered)
Done. Done. (sorting step adds significant CPU time)

Interview trap: "How many columns should you Z-ORDER by?"

  • Practical limit: 2-4 columns. More columns dilute the effectiveness.
  • Each additional column exponentially reduces the clustering benefit for other columns.
  • Rule of thumb: Z-ORDER by the columns most frequently used in WHERE clauses.

SECTION 2: Liquid Clustering vs Z-ORDER vs Partitioning -- The Complete Decision Guide

Answer First: Liquid clustering declares clustering keys without static partition directories, and OPTIMIZE rewrites files as needed to improve clustering. Changing keys does not immediately rewrite the table, and updated rows are not automatically reclustered.

Memory Map: partitioning, Z-ORDER, and Liquid Clustering. When do you use each -> low-cardinality stable columns can use directories -> selected predicates can benefit from multidimensional ordering -> evolving workloads favor adaptive keys -> pruning and maintenance cost decide among them [DB_06_Delta_Lake_Advanced_Masterclass.md:172].

Q4: Compare partitioning, Z-ORDER, and Liquid Clustering. When do you use each?

Answer -- this is one of the most commonly asked "modern Delta" questions:

Partitioning Z-ORDER Liquid Clustering
───────────── ────────── ──────────────────
How it works Splits data into Reorders data Incrementally clusters
physical directories within files using data using Hilbert
(one dir per value) Z-order curves curves
Applied when At WRITE time At OPTIMIZE time At OPTIMIZE time
(every write creates (only runs when you (incremental -- only
partition dirs) run OPTIMIZE) reclusters files as needed)
Can change keys? NO -- requires full YES -- just run YES -- just ALTER TABLE
table rewrite OPTIMIZE with new and next OPTIMIZE applies
ZORDER BY clause new clustering
Works well when Cardinality < 1000 Cardinality is ANY cardinality
(e.g., date, region) medium-high (works for everything)
Failure mode Too many partitions Full rewrite each None major -- designed
= small file problem OPTIMIZE run (not to replace both
(millions of tiny incremental) partitioning and Z-ORDER
files)
Concurrent writes Can cause conflicts Requires full Incremental -- less
on same partition table lock during contention
OPTIMIZE
Supported in OSS Yes Yes Delta Lake 3.x+ (OSS)
Delta? Databricks Runtime 13.3+

The key insight that wins interviews:

Liquid Clustering is incremental: OPTIMIZE avoids rewriting files that are already sufficiently clustered, but it does not promise to touch only newly written rows.

Z-ORDER behavior:
Run 1: Reads 100 GB, Z-orders all 100 GB, writes 100 GB -> 100 GB of I/O
Run 2: Reads 110 GB (100 GB old + 10 GB new), Z-orders all 110 GB, writes 110 GB -> 110 GB of I/O
Run 3: Reads 120 GB, Z-orders all 120 GB -> 120 GB of I/O
(Every run rewrites EVERYTHING -- hugely expensive at scale)
Liquid Clustering behavior:
Run 1: OPTIMIZE clusters the files selected for the initial layout.
Later runs: OPTIMIZE rewrites files as necessary for new keys or unclustered records.
Files that already satisfy clustering requirements can be skipped.
(The actual rewrite volume is determined by file state, not merely row age.)

Answer First: A Delta table can combine PARTITION BY with Z-ORDER-based layout, but liquid CLUSTER BY replaces static partitioning rather than layering on it. The table protocol and creation syntax enforce the valid combination.

Memory Map: you enable Liquid Clustering? Can you migrate an existing table -> table creation declares clustering keys -> ALTER can change keys for future maintenance -> subsequent optimization reorganizes affected files -> protocol and detail output confirm enablement [DB_06_Delta_Lake_Advanced_Masterclass.md:227].

Q5: How do you enable Liquid Clustering? Can you migrate an existing table?

Answer:

sql
-- Creating a NEW table with Liquid Clustering:
CREATE TABLE bookings (
    booking_id STRING,
    flight_date DATE,
    origin STRING,
    destination STRING,
    fare DECIMAL(10,2)
)
USING DELTA
CLUSTER BY (flight_date, origin);
-- NOTE: No PARTITIONED BY! Liquid Clustering replaces partitioning.

-- Changing clustering columns on an existing LC table:
ALTER TABLE bookings CLUSTER BY (flight_date, destination);
-- Next OPTIMIZE will apply the new clustering. No full rewrite needed!

-- Removing clustering entirely:
ALTER TABLE bookings CLUSTER BY NONE;

-- Migrating an existing PARTITIONED table to Liquid Clustering:
-- This is the tricky part. As of 2025, you CANNOT directly convert
-- a partitioned table to Liquid Clustering. You must:
-- 1. CREATE a new table with CLUSTER BY
-- 2. INSERT INTO new_table SELECT * FROM old_table
-- 3. Drop the old table and rename
-- This is a FULL data rewrite.

Common interview trap: "Can a table have both PARTITION BY and CLUSTER BY?"

  • No. They are mutually exclusive. A table is either partitioned or liquid-clustered.
  • If you try to use both, you get an error.

Follow-up trap: "What happens to existing Z-ORDER if I enable Liquid Clustering?"

  • You cannot enable Liquid Clustering on a table that already exists with PARTITIONED BY.
  • For non-partitioned tables: you can ALTER TABLE ... CLUSTER BY (cols) to enable it.
  • Once enabled, OPTIMIZE ... ZORDER BY syntax is no longer allowed on that table.

Answer First: Both are space-filling curves -- mathematical constructs that map multi-dimensional data to a single dimension while preserving locality (nearby points in N-D space stay close in 1-D).

Memory Map: the Hilbert curve used in Liquid Clustering? How is it different from Z-ORDER -> space-filling curve maps multiple dimensions to one ordering -> nearby coordinate values tend to colocate -> alternative curve preserves locality differently -> pruning benchmarks determine practical impact [DB_06_Delta_Lake_Advanced_Masterclass.md:271].

Q6: What is the Hilbert curve used in Liquid Clustering? How is it different from Z-ORDER?

Answer:

Both are space-filling curves -- mathematical constructs that map multi-dimensional data to a single dimension while preserving locality (nearby points in N-D space stay close in 1-D).

πŸ“ Architecture Diagram
Z-ORDER curve:                    Hilbert curve:
β”Œβ”€β”€β”¬β”€β”€β”                          β”Œβ”€β”€β”¬β”€β”€β”
β”‚1 β”‚2 β”‚                          β”‚1 β”‚2 β”‚
β”œβ”€β”€β”Όβ”€β”€β”€                          β”œβ”€β”€β”Όβ”€β”€β”€
β”‚3 β”‚4 β”‚                          β”‚4 β”‚3 β”‚
β””β”€β”€β”΄β”€β”€β”˜                          β””β”€β”€β”΄β”€β”€β”˜

Z-ORDER has "jumps" --           Hilbert has NO jumps --
the curve jumps from             the curve is continuous.
point 2 to point 3              Every step moves to an
(diagonal jump).                 adjacent cell.

Result: Z-ORDER has              Result: Hilbert curve
occasional large gaps            preserves locality better
in data locality.                = better data skipping.

Why this matters for interviews:

  • Hilbert curve provides ~10-20% better data skipping effectiveness compared to Z-ORDER on the same data
  • Liquid Clustering uses Hilbert curves, which is one reason it outperforms Z-ORDER
  • You do NOT need to understand the math -- just know: "Hilbert curves preserve multi-dimensional locality better than Z-order curves because they avoid diagonal jumps"

SECTION 6: Predictive Optimization

Q15: What is Predictive Optimization? How does it work?

Answer:

Predictive Optimization (PO) is a Databricks-managed feature that automatically runs OPTIMIZE and VACUUM on your Delta tables based on usage patterns. Instead of you scheduling these operations manually, Databricks analyzes your table's characteristics and runs maintenance when it is most beneficial.

How it works internally:

πŸ—‚οΈDatabricks' backend service continuously monitors each table:
Signals monitored:
File size distribution (many small files? -> needs OPTIMIZE)
Number of deletion vectors (many DVs? -> needs OPTIMIZE to purge)
Time since last VACUUM (stale files accumulating? -> needs VACUUM)
Query patterns (which tables are queried most? -> prioritize those)
Write patterns (streaming vs batch? -> adjust timing)
Table size (large tables benefit more from compaction)
Decision engine:
Scores each table on "maintenance urgency"
Schedules OPTIMIZE and VACUUM during low-usage periods
Uses Databricks' serverless compute (no cost to your clusters)
Runs incrementally (targets specific partitions, not full table)

How to enable:

sql
-- Enable for a specific table:
ALTER TABLE my_table SET TBLPROPERTIES (
    'delta.enableOptimizeWrite' = true,
    'delta.autoOptimize.optimizeWrite' = true,
    'delta.autoOptimize.autoCompact' = true
);

-- Predictive Optimization is enabled at the catalog/schema level in Unity Catalog:
-- Go to Catalog Explorer -> select schema -> enable Predictive Optimization

-- Or via SQL (Databricks-specific):
ALTER SCHEMA my_schema ENABLE PREDICTIVE OPTIMIZATION;

Key differentiator -- three levels of auto-optimization:

πŸ—‚οΈLevel 1: Optimized Writes (spark.databricks.delta.optimizeWrite.enabled = true)
Happens AT WRITE TIME
Coalesces small partitions into larger files during the write itself
No separate OPTIMIZE needed for writes
Trade-off: slightly slower writes for better file sizes
Best for: streaming workloads with many small batches
Level 2: Auto Compaction (spark.databricks.delta.autoCompact.enabled = true)
Happens AFTER EACH WRITE
Triggers a mini-OPTIMIZE if small files are detected after a write
Runs on the SAME cluster as the write
Trade-off: uses your cluster compute; may slow down subsequent writes
Best for: batch workloads where you want files compacted immediately
Level 3: Predictive Optimization (managed service)
Happens ASYNCHRONOUSLY (background service)
Uses Databricks serverless compute (not your clusters)
Runs OPTIMIZE, VACUUM, and ANALYZE automatically
Intelligent scheduling based on cost-benefit analysis
No trade-off on write latency
Best for: everything. This is the recommended approach.

Answer First: Predictive Optimization applies only to eligible Unity Catalog managed tables and chooses maintenance from observed table activity. It does not remove the need to verify eligibility, operation history, cost, and workload-specific results.

Memory Map: the interview gotchas with Predictive Optimization -> automation applies only to eligible managed tables -> platform chooses timing rather than user schedule -> workload may still incur maintenance cost -> operation history prevents assuming free optimization [DB_06_Delta_Lake_Advanced_Masterclass.md:836].

Q16: What are the interview gotchas with Predictive Optimization?

Answer:

Gotcha 1: "Optimized Writes" is NOT the same as "Predictive Optimization"

Candidates constantly confuse these. Key differences:
Optimized Writes: happens during write, on YOUR cluster, synchronous
Predictive Optimization: happens in background, on DATABRICKS' infra, async
They are complementary -- you can (and should) use both.

Gotcha 2: Predictive Optimization requires Unity Catalog

If you are using Hive Metastore, you CANNOT use Predictive Optimization.
It is a Unity Catalog-only feature.
This is a common interview qualifier: "What are the prerequisites?"

Gotcha 3: Cost visibility

Predictive Optimization uses serverless compute.
The cost appears in your Databricks bill under "serverless compute."
There is no separate SKU -- it is charged at serverless rates.
You can see what PO has done via:
System tables: system.storage.predictive_optimization_operations_history
Catalog Explorer: maintenance history tab

Gotcha 4: It might VACUUM data you still need

Predictive Optimization runs VACUUM with the default 7-day retention.
If your workflow depends on time travel beyond 7 days, PO might
break it by cleaning up old versions.
Fix: Set delta.deletedFileRetentionDuration on the table
to a longer period. PO respects this setting.

SECTION 7: The Small File Problem -- Root Causes and Real Solutions

Answer First: The "small file problem" is when a Delta table contains thousands or millions of small Parquet files (each < 10-100 MB) instead of a few large files (~1 GB each). This is one of the most common production issues with Delta Lake.

Memory Map: the small file problem in Delta Lake. What causes it -> frequent narrow or streaming writes create many fragments -> metadata and object-store requests dominate scans -> tiny tasks waste scheduler overhead -> inventory connects cause to latency [DB_06_Delta_Lake_Advanced_Masterclass.md:878].

Q17: Explain the small file problem in Delta Lake. What causes it?

Answer:

The "small file problem" is when a Delta table contains thousands or millions of small Parquet files (each < 10-100 MB) instead of a few large files (~1 GB each). This is one of the most common production issues with Delta Lake.

Why small files are bad:

πŸ—‚οΈExample: 10 GB of data
Scenario A: 10 files x 1 GB each
File listing: 10 API calls to cloud storage
Task scheduling: 10 Spark tasks
File open overhead: 10 file opens
Total query time: ~30 seconds
Scenario B: 10,000 files x 1 MB each
File listing: 10,000 API calls to cloud storage (SLOW!)
Task scheduling: 10,000 Spark tasks (scheduling overhead)
File open overhead: 10,000 file opens (connection overhead)
Total query time: ~5 minutes (10x slower!)
Driver OOM risk: tracking 10,000 tasks uses significant memory

Root causes (ALL of them -- interviewers want comprehensive answers):

πŸ—‚οΈCause 1: Streaming micro-batches
Spark Structured Streaming writes every trigger interval (10s, 30s, etc.)
Each micro-batch creates 1+ file per partition
24 hours * 60 minutes * 2 writes/min = 2,880 files/day PER PARTITION
After 30 days: 86,400 files per partition
Cause 2: Over-partitioning (HIGH cardinality partition column)
PARTITIONED BY (customer_id) -- 100,000 unique customers
Each write creates 1 file per partition = 100,000 tiny files per write
"I have 5 million files and my table is only 50 GB"
Rule: partition column should have < 1,000 distinct values
Cause 3: Many parallel writers
20 notebooks writing to the same table simultaneously
Each writer creates its own set of files
No coordination between writers on file sizes
Result: 20 small files per write instead of 1 large file
Cause 4: Frequent UPDATEs/DELETEs without Deletion Vectors
Each UPDATE rewrites affected files
If only 1 row changes in a 1 GB file, a new 1 GB file is created
Old file is logically removed but physically exists until VACUUM
Over time: file count doubles, triples
(Deletion Vectors solve this specific cause)
Cause 5: Incorrect repartition before write
df.repartition(1000).write... -- creates exactly 1000 files
If your data is only 5 GB, that is 1000 x 5 MB = 5 MB each (too small!)
Fix: repartition based on data size, not arbitrary numbers
Better: let Spark decide, or use coalesce()
Cause 6: Schema evolution adding columns
Each schema change creates new files with the new schema
Frequent schema changes = many small file batches
Less common but worth mentioning

Answer First: Interview answer framework: "To fix the small file problem, I use a combination of: (1) Optimized Writes to prevent small files at write time, (2) Auto Compaction as an immediate post-write fix, (3) scheduled OPTIMIZE with WHERE clauses for targeted compaction, and (4) Predictive Optimization for hands-off long-term maintenance."

Memory Map: Give me the complete playbook to fix the small file problem -> optimized writes reduce future fragmentation -> auto compaction handles recent output -> scheduled rewrite repairs existing files -> file inventory and query latency validate the playbook [DB_06_Delta_Lake_Advanced_Masterclass.md:945].

Q18: Give me the complete playbook to fix the small file problem.

Answer:

SOLUTION 1: OPTIMIZE (reactive fix)
──────────────────────────────────
-- Run OPTIMIZE on affected partitions
OPTIMIZE my_table WHERE date_col >= '2026-03-01';
Pros: Simple, effective, works on any table
Cons: Reactive (files are already small), costs compute, requires scheduling
When: Daily maintenance window, after batch loads
SOLUTION 2: Optimized Writes (preventive)
──────────────────────────────────────────
-- Enable at table level:
ALTER TABLE my_table SET TBLPROPERTIES (
'delta.autoOptimize.optimizeWrite' = true
);
-- Or at session level:
SET spark.databricks.delta.optimizeWrite.enabled = true;
How it works:
Before writing, Spark looks at the target partition file sizes
If files would be too small, it coalesces partitions internally
Adds ~5-10% write latency but prevents small files at the source
SOLUTION 3: Auto Compaction (reactive but automatic)
────────────────────────────────────────────────────
ALTER TABLE my_table SET TBLPROPERTIES (
'delta.autoOptimize.autoCompact' = true
);
How it works:
After each write commits, checks if small files were created
If yes, runs a mini-OPTIMIZE on the affected partitions
Uses your cluster's compute
SOLUTION 4: Predictive Optimization (set-and-forget)
────────────────────────────────────────────────────
ALTER SCHEMA my_schema ENABLE PREDICTIVE OPTIMIZATION;
How it works:
Databricks monitors and runs OPTIMIZE when beneficial
Uses serverless compute (not your clusters)
Best option if you have Unity Catalog
SOLUTION 5: Coalesce/Repartition at write time
──────────────────────────────────────────────
# Before writing, control file count:
target_file_count = total_data_size_bytes // (1024 * 1024 * 1024) # 1 GB per file
target_file_count = max(target_file_count, 1) # At least 1 file
df.coalesce(target_file_count).write.format("delta").save(path)
# For partitioned tables:
df.repartition("partition_col") \
.write.format("delta") \
.partitionBy("partition_col") \
.save(path)
SOLUTION 6: Reduce partition cardinality
────────────────────────────────────────
-- Bad: 365 partitions/year (too many for small data)
PARTITIONED BY (event_date)
-- Better: 12 partitions/year
PARTITIONED BY (event_month)
-- Best: Use Liquid Clustering instead of partitioning
CLUSTER BY (event_date)
SOLUTION 7: For streaming -- increase trigger interval
──────────────────────────────────────────────────────
# Bad: trigger every 10 seconds (creates many tiny files)
.trigger(processingTime="10 seconds")
# Better: trigger every 5 minutes (fewer, larger files)
.trigger(processingTime="5 minutes")
# Best: use availableNow for micro-batch-like behavior
.trigger(availableNow=True)

Interview answer framework: "To fix the small file problem, I use a combination of: (1) Optimized Writes to prevent small files at write time, (2) Auto Compaction as an immediate post-write fix, (3) scheduled OPTIMIZE with WHERE clauses for targeted compaction, and (4) Predictive Optimization for hands-off long-term maintenance."

SECTION 8: VACUUM -- Risks, Production Incidents, and Gotchas

Answer First: Critical detail: VACUUM is a destructive, irreversible operation. Deleted files cannot be recovered (unless you have cloud storage versioning enabled as a safety net).

Memory Map: What does VACUUM actually do? Walk me through the mechanics -> snapshot identifies files no longer referenced -> age threshold filters eligible objects -> safety check protects active readers and history -> physical deletion makes removal irreversible [DB_06_Delta_Lake_Advanced_Masterclass.md:1037].

Q19: What does VACUUM actually do? Walk me through the mechanics.

Answer:

πŸ“‹ Overview
VACUUM my_table RETAIN 168 HOURS -- 168 hours = 7 days (default)
Step 1: Read the transaction log to identify ALL currently active files
(files referenced by the latest version of the table)
Step 2: List ALL physical files in the table directory on storage
(every Parquet file, including old versions)
Step 3: Compare the two lists:
Active files: {file_A, file_B, file_C} (current version)
All physical files: {file_A, file_B, file_C, file_D, file_E, file_F}
Candidates for deletion: {file_D, file_E, file_F}
Step 4: Filter candidates by retention period:
file_D: modified 2 days ago -> KEEP (within 7-day retention)
file_E: modified 10 days ago -> DELETE (older than 7 days)
file_F: modified 30 days ago -> DELETE (older than 7 days)
Step 5: Physically delete the files from cloud storage
DELETE file_E
DELETE file_F
Step 6: VACUUM does NOT create a new transaction log entry
(it only deletes physical files, not logical references)

Critical detail: VACUUM is a destructive, irreversible operation. Deleted files cannot be recovered (unless you have cloud storage versioning enabled as a safety net).

Answer First: Incident 1: "We lost our time travel".

Memory Map: the real-world VACUUM risks? Give me actual production incident scenarios -> first causal signal anchors triage -> plan, logs, and metrics isolate one cause -> smallest safe change addresses it -> same evidence verifies recovery [DB_06_Delta_Lake_Advanced_Masterclass.md:1072].

Q20: What are the real-world VACUUM risks? Give me actual production incident scenarios.

Answer -- this is where the interview gets intense:

Incident 1: "We lost our time travel"

Situation:
Data team sets VACUUM RETAIN 0 HOURS (to save storage costs)
They run VACUUM at 2 AM every day
At 2:30 AM, an analyst reports: "I need to query yesterday's version"
Result: ALL historical files are gone. Time travel is broken.
Root cause: VACUUM with 0-hour retention deletes ALL non-current files.
No safety net -- you cannot go back to any previous version.
Prevention:
NEVER use retention < 7 days (168 hours) in production
Delta Lake's default safety check prevents retention < 168 hours
To override, you must explicitly set:
spark.databricks.delta.retentionDurationCheck.enabled = false
-- This should NEVER be false in production without extreme caution

Incident 2: "Long-running query failed after VACUUM"

Situation:
Query Q starts at 1:00 PM, reading from table version 50
Q is a complex aggregation that takes 3 hours
At 2:00 PM, VACUUM runs and deletes old files
At 3:00 PM, Q tries to read a file that VACUUM already deleted
Result: FileNotFoundException! Query crashes.
Root cause: Snapshot isolation guarantees a consistent VIEW of the table,
but it does NOT prevent the physical files from being deleted while a
query is still reading them.
Timeline:
1:00 PM: Q starts. Snapshot = version 50. Needs files A, B, C, D.
1:30 PM: New data written, version 51 replaces file B with file B'.
2:00 PM: VACUUM runs. File B is no longer in version 51. File B was
modified >7 days ago. VACUUM deletes file B.
2:30 PM: Q tries to read file B. FILE NOT FOUND. Query fails.
Prevention:
Set retention period > longest-running query duration
If queries can run 24 hours, set VACUUM RETAIN 192 HOURS (8 days)
Monitor query durations and set retention accordingly

Incident 3: "VACUUM deleted files from concurrent write"

Situation:
Writer A starts a large write at 1:00 PM (takes 2 hours)
Writer A creates temp files in the table directory
At 1:30 PM, VACUUM runs and sees these temp files as "not in log"
VACUUM deletes them
At 3:00 PM, Writer A tries to commit. Its files are gone.
Result: Write failure. Data loss.
Root cause: VACUUM identifies files not in the transaction log as garbage.
But Writer A's files are not in the log yet (the write hasn't committed).
Prevention:
This is why the default 7-day retention exists
Temp files from writes in progress are newer than 7 days, so they
survive VACUUM with default retention
NEVER use 0-hour retention, especially with concurrent writers

Incident 4: "Storage costs exploded after enabling VACUUM"

Wait, shouldn't VACUUM REDUCE storage? Yes, but...
Situation:
Table has 10 TB of data. Update-heavy (50% of rows change weekly).
Without VACUUM: old files accumulate -> 10 TB active + 30 TB old = 40 TB
Someone enables VACUUM with 7-day retention
VACUUM runs... and cloud storage egress charges spike
Root cause: VACUUM must LIST all files (expensive on S3/ADLS for large dirs)
and then DELETE them (delete API calls). For 40 TB of accumulated files:
Listing millions of files: takes hours, costs $$$
Deleting millions of files: throttled by cloud storage API limits
Prevention:
Run VACUUM regularly (not once after years of accumulation)
First VACUUM on a neglected table: run in off-peak hours
Consider doing it partition by partition if table is huge

Answer First: Safe VACUUM practice preserves the configured retention window, accounts for concurrent readers and streams, reviews dry-run candidates, and schedules deletion after file-rewriting maintenance has committed. Validate history requirements before physical removal.

Memory Map: the essential VACUUM best practices -> retention exceeds longest reader and recovery window -> dry-run inventory reviews candidates -> concurrent workloads are coordinated -> history and storage checks confirm safe cleanup [DB_06_Delta_Lake_Advanced_Masterclass.md:1164].

Q21: What are the essential VACUUM best practices?

Answer:

DO:
βœ“ Run VACUUM regularly (daily or weekly) -- don't let files accumulate
βœ“ Keep the default retention period (7 days / 168 hours) or longer
βœ“ Schedule VACUUM after OPTIMIZE (OPTIMIZE first creates large files,
VACUUM then cleans up the old small files)
βœ“ Monitor VACUUM duration and set alerts if it takes too long
βœ“ Use Predictive Optimization to automate VACUUM scheduling
βœ“ Consider cloud storage lifecycle policies as a safety net
DON'T:
βœ— NEVER set retentionDurationCheck.enabled = false in production
βœ— NEVER use RETAIN 0 HOURS unless you have a VERY specific reason
βœ— Don't run VACUUM while long-running queries are active
βœ— Don't run VACUUM and OPTIMIZE simultaneously (OPTIMIZE first, then VACUUM)
βœ— Don't assume VACUUM is instant -- it can take hours on large tables
PRODUCTION CHECKLIST
1. Set table property: delta.deletedFileRetentionDuration = "168 hours"
2. Schedule: OPTIMIZE at 1 AM, VACUUM at 3 AM (after OPTIMIZE finishes)
3. Alert: if VACUUM takes > 2x its usual duration
4. Audit: check VACUUM history via DESCRIBE HISTORY my_table
5. Safety net: enable cloud storage soft-delete/versioning (ADLS/S3)
so accidentally VACUUMed files can be recovered within 30 days

Answer First: This is the single feature Iceberg fans use to argue Iceberg is better than Delta. Understand it deeply.

Memory Map: the Iceberg "hidden partitioning" advantage that interviewers always ask about -> logical transform derives partition values from source columns -> engine evolves transforms without exposing directory logic -> queries prune through metadata -> schema evolution avoids manual partition rewrites [DB_06_Delta_Lake_Advanced_Masterclass.md:1262].

Q24: What is the Iceberg "hidden partitioning" advantage that interviewers always ask about?

Answer:

This is the single feature Iceberg fans use to argue Iceberg is better than Delta. Understand it deeply.

DELTA LAKE PARTITIONING
CREATE TABLE events
PARTITIONED BY (event_date DATE)
Problem: You partitioned by DATE. Now you realize you should partition by MONTH.
Fix: Full table rewrite. Create new table with PARTITIONED BY (event_month).
Copy all data. Drop old table. Rename.
For 50 TB: this is a multi-hour, expensive operation.
ICEBERG HIDDEN PARTITIONING
CREATE TABLE events (
event_timestamp TIMESTAMP,
...
) USING ICEBERG
PARTITIONED BY (months(event_timestamp)) -- partition by month, derived from timestamp
Benefit 1: Users write queries using event_timestamp (the real column).
They never need to know the partition column exists.
SELECT * FROM events WHERE event_timestamp > '2026-01-01'
-- Iceberg automatically prunes partitions. No explicit partition filter needed.
Benefit 2: You can EVOLVE the partition scheme:
ALTER TABLE events ADD PARTITION FIELD days(event_timestamp)
-- New data is partitioned by DAY. Old data stays partitioned by MONTH.
-- No rewrite needed! Iceberg handles both schemes transparently.
DELTA LAKE'S RESPONSE:
Liquid Clustering addresses most of this:
No physical partitions to manage
Clustering columns can be changed via ALTER TABLE
No full rewrite needed to change clustering
But: Liquid Clustering is NOT partitioning -- it is a different approach
(it clusters data within files, not across directories)

Interview trap: "Is Iceberg hidden partitioning strictly better than Delta?" Answer: "For partition evolution, yes -- Iceberg handles it more gracefully. But Delta's Liquid Clustering sidesteps the problem entirely by removing the need for explicit partitioning. For greenfield projects on Databricks, Liquid Clustering is the recommended approach, which makes partition evolution a non-issue."

Answer First: OPTIMIZE and VACUUM can overlap, and a safe retention interval prevents newly removed files from becoming immediate VACUUM candidates. The real concurrency danger is an unsafe interval shorter than the longest-running reader, writer, or stream lag; sequential scheduling is still simpler to operate and verify.

Memory Map: Can you run OPTIMIZE and VACUUM at the same time -> OPTIMIZE commits replacement files and tombstones -> retention age keeps fresh tombstones ineligible for deletion -> unsafe retention can outpace active readers or writers -> DRY RUN and operation history verify safe cleanup [DB_06_Delta_Lake_Advanced_Masterclass.md:1332].

Q26: Can you run OPTIMIZE and VACUUM at the same time?

Current guardrail: With safe retention, newly removed or unreferenced files are not VACUUM candidates. Keep the retention interval longer than the longest-running concurrent transaction and maximum stream lag. See Delta Lake VACUUM documentation.

Answer:

Technically: Yes, they can run concurrently (no hard lock).
Operationally: Prefer separate schedules so resource use and evidence are easier to interpret.
Safety boundary:
OPTIMIZE creates replacement files and tombstones the old files.
With a safe retention interval, those newly removed files are not VACUUM candidates.
Risk appears when retention is shorter than a concurrent reader, writer, or stream lag;
VACUUM can then remove a file that the active operation still needs.
Best practice: Keep retention longer than every concurrent operation, use VACUUM DRY RUN,
and schedule OPTIMIZE before VACUUM when you want simpler operations and diagnostics.
Schedule them sequentially, not in parallel.

SECTION 4: OPTIMIZE, VACUUM, AND THE SMALL FILE PROBLEM

Answer First: What it does: Combines many small files into fewer large files (~1 GB each).

Memory Map: OPTIMIZE? When is it good? When is it bad -> candidate fragments are read into a rewrite job -> rows repack into fewer target-sized files -> atomic commit publishes replacements -> benefit must exceed compute and write amplification [Delta_01_Complete_Guide.md:608].

Q11 β€” What is OPTIMIZE? When is it good? When is it bad?

Question: "Explain OPTIMIZE. What are its trade-offs?"

What it does: Combines many small files into fewer large files (~1 GB each).

sql
-- Basic OPTIMIZE
OPTIMIZE orders;
-- Example: 10,000 files Γ— 1 MB each β†’ 10 files Γ— 1 GB each

-- OPTIMIZE specific partition
OPTIMIZE orders WHERE order_date >= '2026-03-01';

-- OPTIMIZE with Z-ORDER (organize data within files)
OPTIMIZE orders ZORDER BY (customer_id, order_date);

GOOD things about OPTIMIZE:

BenefitWhy
Faster readsFewer files = fewer I/O operations = faster queries
Better data skippingSorted data has tighter min/max ranges per file
Faster MERGEFewer files = fewer tasks for Spark
Non-destructiveOld files still exist (for time travel) until VACUUM

BAD things about OPTIMIZE:

DownsideWhy
Rewrites ALL filesEven unchanged files get rewritten (Z-ORDER does full rewrite)
Expensive for large tables1 TB table = reads 1 TB + writes 1 TB = 2 TB of I/O
Doubles storage temporarilyOld files + new files exist until VACUUM
Not incremental (with Z-ORDER)OPTIMIZE without Z-ORDER is incremental in newer versions
Blocks concurrent writes brieflyTakes a commit lock during the commit phase

Best practices:

WHEN to OPTIMIZE:
βœ… After large batch writes (ETL job just loaded 10M rows)
βœ… Before heavy read workloads (BI dashboard refresh coming)
βœ… When MERGE is slow (too many small files in target)
βœ… Daily maintenance window (schedule nightly)
WHEN NOT to OPTIMIZE:
❌ After every micro-batch in streaming (use autoCompact instead)
❌ On tables with very few files already
❌ During peak read hours (it competes for resources)

Interview Tip: "OPTIMIZE is a WRITE operation that benefits READS. Run it during low-traffic windows. For streaming tables, use autoOptimize table property instead of manual OPTIMIZE."

Answer First: Rule: NEVER use RETAIN 0 HOURS in production. Default 7 days is a safety net.

Memory Map: VACUUM? What are the risks -> retention threshold protects readers and rollback -> expired unreferenced files become candidates -> safety guard rejects dangerously short windows -> deletion cannot be undone without backup [Delta_01_Complete_Guide.md:664].

Q12 β€” What is VACUUM? What are the risks?

Question: "What does VACUUM do? Can I run VACUUM RETAIN 0 HOURS? What happens?"

What VACUUM does:

πŸ—‚οΈBEFORE VACUUM:
orders/
_delta_log/
part-00000-NEW.parquet ← Current (referenced by latest commit)
part-00001-NEW.parquet ← Current
part-00000-OLD.parquet ← Old (was replaced by OPTIMIZE 10 days ago)
part-00001-OLD.parquet ← Old (was replaced by UPDATE 8 days ago)
AFTER VACUUM (7-day retention):
orders/
_delta_log/
part-00000-NEW.parquet ← Kept (current)
part-00001-NEW.parquet ← Kept (current)
part-00000-OLD.parquet ← DELETED (older than 7 days, unreferenced)
part-00001-OLD.parquet ← DELETED (older than 7 days, unreferenced)
sql
-- Default: delete files older than 7 days
VACUUM orders;

-- Custom retention
VACUUM orders RETAIN 168 HOURS;    -- 168 hours = 7 days

-- DANGEROUS β€” delete ALL old files immediately
SET spark.databricks.delta.retentionDurationCheck.enabled = false;
VACUUM orders RETAIN 0 HOURS;

Risks of VACUUM RETAIN 0 HOURS:

RiskExplanation
Breaks ALL time travelCan't query any previous version β€” old files are gone
Active readers may failA running query that started before VACUUM may reference deleted files β†’ FileNotFoundException
No recoveryDeleted files are gone permanently β€” no undo
Concurrent write failuresWriters that started with old versions lose their reference files

Rule: NEVER use RETAIN 0 HOURS in production. Default 7 days is a safety net.

OPTIMIZE vs VACUUM β€” they work as a pair:

Step 1: OPTIMIZE→Creates NEW large files, marks OLD small files as "removed"
Step 2: VACUUM→Physically DELETES the OLD files marked as "removed"
Without VACUUM after OPTIMIZE: Storage keeps growing (old + new files both exist)
Without OPTIMIZE before VACUUM: Nothing to clean up (no old files to remove)

Answer First: The small-file problem occurs when frequent narrow writes create far more files than useful parallelism requires, increasing listing, scheduling, and scan overhead. Measure file-size distribution, compact eligible files, and prevent recurrence at the writer.

Memory Map: The Small File Problem β€” Root Causes and Solutions -> small frequent writes inflate file count -> open requests and task scheduling dominate useful work -> optimized writing prevents new fragments -> compaction repairs the backlog [Delta_01_Complete_Guide.md:721].

Q13 β€” The Small File Problem β€” Root Causes and Solutions

Question: "Your Delta table has 50,000 small files (each <1 MB). Queries take 45 minutes. How do you fix it?"

Why small files are bad:

🧠 Memory Map
50,000 files Γ— 1 MB each = 50 GB of data
Each file requires:
β†’ 1 Spark task to read
β†’ 1 metadata operation on cloud storage
β†’ Scheduler overhead
50,000 tasks vs 50 tasks (with 1 GB files) = ~100x slower

Root causes of small files:

CauseExample
Streaming micro-batchesProcessing every 10 seconds β†’ 6 files/minute β†’ 8,640 files/day
Over-partitioningPARTITIONED BY (date, hour, region, category) β†’ millions of tiny partitions
Frequent small appends100-row inserts every minute
Too many shuffle partitionsspark.sql.shuffle.partitions = 200 writes 200 files per write

Immediate fix:

sql
-- Step 1: Compact files now
OPTIMIZE slow_table;
-- 50,000 Γ— 1 MB β†’ ~50 Γ— 1 GB

-- Step 2: Clean up old files
VACUUM slow_table;

-- Step 3: Refresh statistics
ANALYZE TABLE slow_table COMPUTE STATISTICS;

Prevent future small files:

sql
-- Option A: Auto-optimization table properties
ALTER TABLE my_table SET TBLPROPERTIES (
    'delta.autoOptimize.optimizeWrite' = 'true',     -- Coalesce during write
    'delta.autoOptimize.autoCompact' = 'true'          -- Mini-OPTIMIZE after each write
);

-- Option B: For streaming β€” increase trigger interval
# .trigger(processingTime="5 minutes")   -- Instead of "10 seconds"

-- Option C: For new tables β€” use Liquid Clustering
CREATE TABLE orders (...) CLUSTER BY (order_date, customer_id);
-- Handles compaction automatically

-- Option D: Reduce shuffle partitions
spark.conf.set("spark.sql.shuffle.partitions", "auto")  -- Adaptive (AQE)

SECTION 5: DATA ORGANIZATION β€” PARTITIONING vs Z-ORDER vs LIQUID CLUSTERING

Answer First: Column has low cardinality (few unique values): date, country, region.

Memory Map: Partitioning β€” When is it good? When is it bad -> directory values enable coarse pruning -> excessive cardinality creates tiny directories -> skew produces uneven file populations -> stable selective columns justify the technique [Delta_01_Complete_Guide.md:782].

Q14 β€” Partitioning β€” When is it good? When is it bad?

Question: "When should you partition a Delta table? When should you NOT?"

sql
-- Creating a partitioned table
CREATE TABLE bookings (
    booking_id LONG,
    passenger_name STRING,
    booking_date DATE,
    amount DECIMAL(10,2)
) PARTITIONED BY (booking_date);

GOOD β€” Use partitioning when:

  • Column has low cardinality (few unique values): date, country, region
  • Each partition has at least 1 GB of data
  • Most queries filter on the partition column
  • Example: PARTITIONED BY (year) where each year has 50 GB+ of data

BAD β€” Don't partition when:

  • Column has high cardinality (many unique values): user_id, order_id
  • Partitions are too small (< 1 GB each) β†’ small file problem
  • You filter on multiple columns β†’ partitioning only helps ONE column
  • Example: PARTITIONED BY (customer_id) with 10 million customers = disaster

The 1 GB rule:

Total data Γ· Number of unique partition values = Partition size
Example: 500 GB table, partitioned by date, 365 days/year
β†’ 500 GB Γ· 365 = ~1.4 GB per partition βœ… Good!
Example: 500 GB table, partitioned by customer_id, 1 million customers
β†’ 500 GB Γ· 1,000,000 = 0.5 MB per partition ❌ Terrible!

Answer First: Regular sorting ( ORDER BY ) sorts by one column, then another. Z-ORDER uses a space-filling curve to interleave multiple columns, so data is co-located on ALL specified columns simultaneously.

Memory Map: Z-ORDER β€” How does it work internally -> space-filling ordering interleaves selected column bits -> nearby multidimensional values land together -> file statistics describe tighter ranges -> selective filters skip more data [Delta_01_Complete_Guide.md:821].

Q15 β€” Z-ORDER β€” How does it work internally?

Question: "What is Z-ORDER? How is it different from sorting? How many columns can you Z-ORDER on?"

What Z-ORDER does: Regular sorting (ORDER BY) sorts by one column, then another. Z-ORDER uses a space-filling curve to interleave multiple columns, so data is co-located on ALL specified columns simultaneously.

sql
-- Z-ORDER is always used WITH OPTIMIZE
OPTIMIZE orders ZORDER BY (customer_id, order_date);

How it helps β€” data skipping becomes effective:

πŸ“‹ Overview
WITHOUT Z-ORDER:
File 1: customer_id [1-1000000], order_date [2025-01-01 to 2026-12-31]
File 2: customer_id [1-1000000], order_date [2025-01-01 to 2026-12-31]
β†’ Every file has ALL customer_ids and ALL dates
β†’ Query WHERE customer_id = 42β†’must read ALL files (no skipping!)
WITH Z-ORDER on (customer_id):
File 1: customer_id [1-10000]
File 2: customer_id [10001-20000]
β†’ Query WHERE customer_id = 42β†’reads only File 1 (skips rest!)

Limitations of Z-ORDER:

LimitationExplanation
Max 4 columns effectiveMore columns = less effective (diminishing returns)
Full rewrite every timeOPTIMIZE ZORDER rewrites ALL files β€” expensive on large tables
Not incrementalCan't Z-ORDER just the new data β€” must redo everything
Manual operationMust run OPTIMIZE manually or schedule it
Can't change columns easilyChanging Z-ORDER columns requires full rewrite

Answer First: "For new tables, I always use CLUSTER BY instead of PARTITIONED BY. It's incremental, handles any cardinality, and I can change the clustering columns without rewriting the table."

Memory Map: Liquid Clustering β€” The Modern Replacement -> adaptive keys remove rigid directory boundaries -> maintenance incrementally reorganizes touched files -> evolving access patterns can change keys -> reduced rewrite burden supports modern tables [Delta_01_Complete_Guide.md:859].

Q16 β€” Liquid Clustering β€” The Modern Replacement

Question: "What is Liquid Clustering? Why does Databricks say it replaces both partitioning AND Z-ORDER?"

sql
-- Create table with Liquid Clustering
CREATE TABLE orders (
    order_id LONG,
    customer_id LONG,
    order_date DATE,
    amount DECIMAL(10,2)
) CLUSTER BY (order_date, customer_id);

-- Change clustering columns anytime β€” NO full rewrite!
ALTER TABLE orders CLUSTER BY (region, order_date);

-- OPTIMIZE is incremental β€” it rewrites files as necessary for clustering
OPTIMIZE orders;

Why Liquid Clustering is better:

AspectPartitioningZ-OrderingLiquid Clustering
How it worksSeparate folders per valueSorts data within filesAuto-organizes data incrementally
Good forLow cardinality onlyHigh cardinalityAny cardinality
Applied whenOn write (rigid)Manual OPTIMIZEOPTIMIZE or predictive optimization
IncrementalN/ARewrites the Z-ORDER scopeYes (files needing clustering)
Change columnsRequires full table rewriteMust re-OPTIMIZE everythingJust ALTER TABLE
Small file handlingCan cause small filesDoesn't helpPrevents small files
Replaces others?β€”NoYes β€” replaces BOTH

When to use what (2026 recommendation):

🧠 NEW TABLES β†’ Always use Liquid Clustering
NEW TABLESAlways use Liquid Clustering
EXISTING TABLESMigrate to Liquid Clustering when possible
LEGACYKeep partitioning if working fine, don't fix what's not broken

Interview Tip: "For new tables, I always use CLUSTER BY instead of PARTITIONED BY. It's incremental, handles any cardinality, and I can change the clustering columns without rewriting the table."

Q17

Question: "How does Delta Lake skip files during queries? What are file-level statistics?"

sql
SELECT * FROM orders WHERE order_date = '2026-03-15' AND customer_id = 42

Behind the scenes:

πŸ“‹ Overview
Delta reads statistics from the transaction log (NOT the actual files):
File A: min(order_date) = 2026-01-01, max = 2026-01-31, min(customer_id) = 1, max = 10000
β†’ Can order_date = '2026-03-15' be here? NOβ†’SKIP βœ…
File B: min(order_date) = 2026-03-01, max = 2026-03-31, min(customer_id) = 1, max = 100
β†’ Can order_date = '2026-03-15' be here? YES
→ Can customer_id = 42 be here? YES→READ this file
File C: min(order_date) = 2026-03-01, max = 2026-03-31, min(customer_id) = 5000, max = 10000
β†’ Can order_date = '2026-03-15' be here? YES
β†’ Can customer_id = 42 be here? NO (42 < 5000) β†’ SKIP βœ…
Result: Read 1 file instead of 3β†’3x faster!

Key details:

  • Stats stored for the first 32 columns by default
  • Config: delta.dataSkippingNumIndexedCols (default 32)
  • Move frequently filtered columns to the first 32 positions in your schema
  • Works BEST when data is sorted/clustered (Z-ORDER or Liquid Clustering)
  • Stats include: min, max, null count, row count per file

Why Z-ORDER/Clustering improves data skipping:

UNSORTED data: File min=1, max=1000000 (wide range β†’ can't skip anything)
SORTED data: File min=1, max=10000 (narrow range β†’ skip most files!)

Q26 β€” Important Delta Table Properties (Know These!)

Question: "What are the most important Delta table properties?"

sql
ALTER TABLE orders SET TBLPROPERTIES (

    -- AUTO OPTIMIZATION: Fix small files automatically
    'delta.autoOptimize.optimizeWrite' = 'true',
    -- Coalesces small output files during write

    'delta.autoOptimize.autoCompact' = 'true',
    -- Runs mini-OPTIMIZE after each write

    -- CHANGE DATA FEED: Track row-level changes for incremental ETL
    'delta.enableChangeDataFeed' = 'true',

    -- DELETION VECTORS: Faster UPDATE/DELETE
    'delta.enableDeletionVectors' = 'true',

    -- COLUMN MAPPING: Enable rename/drop columns
    'delta.columnMapping.mode' = 'name',

    -- RETENTION: How long to keep old data
    'delta.logRetentionDuration' = 'interval 30 days',
    'delta.deletedFileRetentionDuration' = 'interval 7 days',

    -- TYPE WIDENING: Allow type changes without rewrite
    'delta.enableTypeWidening' = 'true',

    -- UNIFORM: Auto-generate Iceberg metadata
    'delta.universalFormat.enabledFormats' = 'iceberg'
);

Q29 β€” Predictive Optimization β€” Auto-maintenance

Question: "What is Predictive Optimization? How does it work?"

Predictive Optimization is Databricks' auto-maintenance system. It watches your tables and automatically runs OPTIMIZE, VACUUM, and ANALYZE TABLE at the right time.

Key points:

  • Enabled by default on all new Unity Catalog managed tables
  • Learns access patterns β†’ optimizes during low-traffic windows
  • Automatically runs: OPTIMIZE (compaction), VACUUM (cleanup), ANALYZE TABLE (refresh stats)
  • No configuration needed β€” just use managed tables
  • Not available on external tables

Interview Tip: "With Predictive Optimization, I no longer need to schedule nightly OPTIMIZE/VACUUM jobs. The platform handles it. This is one reason I prefer managed tables over external tables."

Answer First: This lab creates a deliberately fragmented table, records the initial file count, runs OPTIMIZE, and compares the resulting file layout and query scan. The before-and-after evidence shows compaction rather than merely asserting it.

Memory Map: LAB 3 β€” Watch OPTIMIZE Fix the Small File Problem -> lab creates a deliberately fragmented baseline -> inventory records file count and size -> rewrite combines rows into larger objects -> before-after scan metrics show effect [Delta_01_Complete_Guide.md:1735].

LAB 3 β€” Watch OPTIMIZE Fix the Small File Problem

python β€” editable
# ─── Create a table with MANY small files (simulating streaming) ─────
small_files_path = "/tmp/delta_lab/small_files"
dbutils.fs.rm(small_files_path, recurse=True)

# Write 10 times, one row each time β†’ 10 tiny files
for i in range(10):
    spark.createDataFrame([Row(id=i, val=f"row_{i}")]) \
         .write.format("delta").mode("append").save(small_files_path)

# Count Parquet files
parquet_files = [f.name for f in dbutils.fs.ls(small_files_path) if f.name.endswith(".parquet")]
print(f"πŸ”΄ Parquet files BEFORE OPTIMIZE: {len(parquet_files)}")
# EXPECTED: πŸ”΄ Parquet files BEFORE OPTIMIZE: 10
#
# 🧠 Each file is tiny (few hundred bytes) β†’ disaster for query performance
python β€” editable
# ─── Run OPTIMIZE ───────────────────────────────────
spark.sql(f"OPTIMIZE delta.`{small_files_path}`")

parquet_files = [f.name for f in dbutils.fs.ls(small_files_path) if f.name.endswith(".parquet")]
print(f"🟒 Parquet files AFTER OPTIMIZE: {len(parquet_files)}")
# EXPECTED: 🟒 Parquet files AFTER OPTIMIZE: 11
#
# Wait β€” 11? Why not 1?
#   β†’ OPTIMIZE created 1 NEW compacted file (with all 10 rows)
#   β†’ The 10 OLD files are marked "removed" in _delta_log but still on disk
#   β†’ VACUUM will clean them up later
#
# Verify: check the log
dt = DeltaTable.forPath(spark, small_files_path)
dt.history().select("version", "operation", "operationMetrics").show(truncate=False)
# Latest row: operation=OPTIMIZE, numFilesAdded=1, numFilesRemoved=10 βœ…

VISUAL ANIMATION 3 β€” Z-ORDER Rearranging Data

πŸ“‹ Overview
BEFORE Z-ORDER on (customer_id):
File 1: [cust_5, cust_102, cust_57, cust_893, cust_12, cust_501] ← random!
File 2: [cust_88, cust_3, cust_421, cust_77, cust_999, cust_15] ← random!
File 3: [cust_66, cust_250, cust_8, cust_180, cust_33, cust_700] ← random!
Query: WHERE customer_id = 102
β†’ Spark must scan ALL 3 files (any could contain 102) 😒
AFTER Z-ORDER on (customer_id):
File 1: [cust_1, cust_3, cust_5, cust_8, cust_12, cust_15] ← sorted range 1-15
File 2: [cust_33, cust_57, cust_66, cust_77, cust_88, cust_102] ← sorted range 33-102
File 3: [cust_180, cust_250, cust_421, cust_501, cust_700, cust_893] ← sorted range 180-893
Query: WHERE customer_id = 102
β†’ Delta checks min/max stats in _delta_log:
File 1: min=1, max=15β†’102 NOT here, skip ⏭️
File 2: min=33, max=102β†’102 COULD be here, read βœ…
File 3: min=180, max=893β†’102 NOT here, skip ⏭️
β†’ Only 1 file read instead of 3! 3x faster πŸš€
🧠 KEY INSIGHT:
Z-ORDER doesn't change HOW data is stored (still Parquet),
it changes WHICH rows end up in WHICH file.
Data skipping uses min/max stats to avoid reading files that can't match.

Answer First: Why it happens: Delta's default minimum retention is 7 days. VACUUM RETAIN 0 HOURS would delete files that RUNNING QUERIES might still be reading β†’ data corruption.

Memory Map: VACUUM RETAIN 0 HOURS β€” "Invalid retention" Error -> minimum safety window rejects destructive zero-hour request -> disabling guard would expose readers and history -> valid retention preserves recovery files -> error therefore protects table correctness [Delta_01_Complete_Guide.md:1881].

Gotcha 1: VACUUM RETAIN 0 HOURS β€” "Invalid retention" Error

Error: IllegalArgumentException: requirement failed: Are you sure you would
like to vacuum files with such a low retention period?
If you're not sure, you can set:
spark.databricks.delta.retentionDurationCheck.enabled = false

Why it happens: Delta's default minimum retention is 7 days. VACUUM RETAIN 0 HOURS would delete files that RUNNING QUERIES might still be reading β†’ data corruption.

Fix:

python β€” editable
# DANGER: only disable if you KNOW no concurrent reads are running
spark.conf.set("spark.databricks.delta.retentionDurationCheck.enabled", "false")
spark.sql(f"VACUUM delta.`{path}` RETAIN 0 HOURS")
# Then TURN IT BACK ON:
spark.conf.set("spark.databricks.delta.retentionDurationCheck.enabled", "true")

Interview trap: If they ask "how would you immediately VACUUM a Delta table?" and you say "RETAIN 0 HOURS," they'll follow up with "What's the risk?" β†’ answer: concurrent readers can get FileNotFoundException.

Answer First: Symptom: Streaming writes create thousands of tiny files (few KB each).

Memory Map: Small Files Problem in Streaming -> micro-batches repeatedly emit tiny outputs -> fragments accumulate faster than maintenance -> optimized writing and compaction reduce creation rate -> file growth monitoring prevents recurrence [Delta_01_Complete_Guide.md:1991].

Gotcha 5: Small Files Problem in Streaming

Symptom: Streaming writes create thousands of tiny files (few KB each)
β†’ queries become slow, $$$ spent on listing files

Fix:

python β€” editable
# Enable auto-compaction + optimized writes on the table
spark.sql(f"""
  ALTER TABLE delta.`{path}`
  SET TBLPROPERTIES (
    'delta.autoOptimize.optimizeWrite' = 'true',
    'delta.autoOptimize.autoCompact'   = 'true'
  )
""")

# Also: run scheduled OPTIMIZE daily
spark.sql(f"OPTIMIZE delta.`{path}`")

Databricks-routed Delta concepts: 05_Performance_Tuning_and_Production

Answer First: Z-ORDER sorts data across multiple columns simultaneously to improve data skipping. Without Z-ORDER, data for a specific customer might be scattered across 1,000 files.

Memory Map: Z-ORDER and how does Liquid Clustering improve on it -> multicolumn ordering improves range locality -> adaptive clustering removes static key layout -> maintenance rewrites only files needing organization -> filter selectivity and bytes scanned quantify improvement [05_Performance_Tuning_and_Production.md:881].

Q12F: What is Z-ORDER and how does Liquid Clustering improve on it?

Simple Explanation: Z-ORDER sorts data across multiple columns simultaneously to improve data skipping. Without Z-ORDER, data for a specific customer might be scattered across 1,000 files. With Z-ORDER on customer_id, all data for one customer is concentrated in a few files β€” queries filter out 99% of files.

Liquid clustering replaces static partitions and Z-ORDER for supported tables by declaring clustering keys that OPTIMIZE applies as needed. Changing keys does not immediately rewrite existing data, and updated rows are not automatically reclustered.

Technical depth:

sql
-- Z-ORDER: Manually triggered, full rewrite
OPTIMIZE my_table ZORDER BY (customer_id, order_date);
-- Rewrites ALL files sorted by these columns
-- Expensive but makes subsequent queries much faster

-- Liquid clustering: declare keys, then run OPTIMIZE as needed
CREATE TABLE my_table (...) CLUSTER BY (customer_id, order_date);
-- OR convert existing table:
ALTER TABLE my_table CLUSTER BY (customer_id, order_date);
-- OPTIMIZE rewrites data files as necessary to apply the current clustering keys
FeatureZ-ORDERLiquid Clustering
TriggerOPTIMIZE ... ZORDER BYOPTIMIZE respects declared clustering keys
ScopeRewrites files selected for Z-ORDERRewrites data files as necessary; updated rows are not automatically reclustered
Change columnsA later Z-ORDER uses the requested columnsALTER TABLE CLUSTER BY; later OPTIMIZE applies the new keys
Partition requiredWorks with partitioned tablesReplaces partitioning entirely
PerformanceExcellent for readsSame read perf, much faster writes

Interview Tip: Say: "For supported new tables, I prefer liquid clustering over static partitioning and Z-ORDER. I still run or schedule OPTIMIZE; the clustering declaration alone does not continuously reorganize updated rows."

What NOT to Say: "I partition by date and Z-ORDER by customer." With Liquid Clustering, you often don't need partitioning at all.

Routed Delta question-bank prompts β€” 40-performance-maintenance

11. What does the OPTIMIZE command do? > Compacts many small Parquet files into fewer, larger files (target ~1 GB each). Improves read performance by reducing file listing overhead and enabling better data skipping.

12. What is Z-ORDER and what problem does it solve? > A multi-dimensional clustering technique applied during OPTIMIZE. Co-locates related data in the same files so queries with filters on Z-ORDERed columns skip more files. Solves the problem of data skipping on non-partition columns.

13. What are Deletion Vectors in Delta Lake? > A bitmap attached to a Parquet file marking which rows are logically deleted/updated. Avoids rewriting the entire file for small DELETE/UPDATE operations β€” marks rows as deleted and rewrites lazily.

17. What is Liquid Clustering in Delta Lake? > A replacement for partitioning + Z-ORDER that declares clustering keys with CLUSTER BY (col1, col2). OPTIMIZE incrementally rewrites files as necessary; writes and updates are not continuously reclustered by the declaration alone. Keys can change without an immediate full-table rewrite.

18. What is Predictive Optimization in Databricks? > An automated maintenance feature for Unity Catalog managed tables that runs OPTIMIZE, VACUUM, and ANALYZE automatically based on table usage patterns. Eliminates the need for manual scheduling of maintenance operations.

3. Compare Z-ORDER vs Liquid Clustering β€” when would you use each?

Key points: Z-ORDER is applied by OPTIMIZE ZORDER BY to its selected scope. Liquid clustering also relies on OPTIMIZE, but incrementally rewrites files as necessary and allows clustering keys to evolve without an immediate full-table rewrite. Databricks recommends liquid clustering for supported new tables.

4. Explain data skipping in Delta Lake. How does it use min/max statistics?

Key points: Each Parquet file stores min/max statistics for the first 32 columns. When a query has a filter (e.g., WHERE date = '2026-03-15'), Delta checks the min/max stats of each file and skips files where the filter value falls outside the range. Z-ORDER/Liquid Clustering improve data skipping by co-locating similar values in the same files, narrowing the min/max ranges.

10. Explain the difference between OPTIMIZE WHERE and partition-level OPTIMIZE. > Key points: OPTIMIZE table WHERE date = '2026-03-15' compacts only files matching the predicate β€” faster, less compute. Partition-level OPTIMIZE targets a specific partition. Both avoid rewriting the entire table. Use WHERE for targeted compaction (e.g., only compact today's partition after ingestion). Full OPTIMIZE rewrites everything β€” expensive on large tables.

11. How does Delta Lake handle small file compaction? What is the "small file problem"? > Key points: Small file problem: many tiny Parquet files (e.g., 50K files at 2 MB each) from streaming writes or frequent appends. Causes slow reads (file listing overhead, poor data skipping). Fix: OPTIMIZE compacts files to ~1 GB. Prevention: enable autoOptimize.optimizeWrite (coalesces during write) and autoOptimize.autoCompact (compacts in background). Predictive Optimization automates this for UC managed tables.

13. How do Deletion Vectors improve UPDATE/DELETE performance compared to the traditional approach? > Key points: Traditional: UPDATE one row in a 1 GB Parquet file β†’ rewrite the entire 1 GB file. With Deletion Vectors: mark the row as deleted in a small bitmap file, write only the new row. Orders of magnitude faster for small updates. Trade-off: reads must check the bitmap (slight overhead). Compaction eventually merges deletions back into the files.

14. What is the relationship between file statistics, data skipping, and Z-ORDER? > Key points: File statistics (min/max per column) enable data skipping (skip files whose min/max range doesn't match the query filter). Z-ORDER tightens the min/max ranges by co-locating similar values in the same files. Without Z-ORDER, a file might contain dates from Jan-Dec (wide range, rarely skipped). With Z-ORDER, each file contains a narrow date range (easily skipped). The three work as a chain: Z-ORDER β†’ tighter stats β†’ more files skipped β†’ faster queries.

18. How does Liquid Clustering handle incremental clustering vs Z-ORDER which requires full rewrite? > Key points: Z-ORDER runs during OPTIMIZE over its selected scope. Liquid clustering is also triggered by OPTIMIZE, which rewrites files as necessary and can skip files that are already sufficiently clustered. Clustering keys are stored in table metadata and can change without an immediate full-table rewrite; existing or updated records are reorganized by later OPTIMIZE work.

19. What are the trade-offs of over-partitioning a Delta table? > Key points: Too many partitions (e.g., partition by customer_id with 1M customers) β†’ millions of directories, tiny files in each partition, slow metadata operations, ineffective OPTIMIZE. Rule of thumb: each partition should have at least 1 GB of data. Better alternative: use Liquid Clustering on high-cardinality columns instead of partitioning. Partition only on low-cardinality columns (date, region) with large data per partition.

Advanced

Delta Lake Scenarios and Labs

#

Delta Lake Scenarios and Labs

Answer First: Delta incident work starts from table history and operation metrics, protects retained evidence, reproduces the state transition, and verifies both data and protocol compatibility.

Memory Map: history -> evidence -> reproduce -> repair -> validate -> prevent recurrence.

Answer First: Table history identifies the last good version, and RESTORE recovers it while the referenced data files remain retained. If VACUUM has deleted those files, recovery requires a clone, backup, or source replay.

Memory Map: Scenario β€” You accidentally deleted critical data 3 days ago. How do you recover -> history locates the last good version -> retained files permit snapshot reconstruction -> RESTORE publishes the recovered state -> row reconciliation confirms critical records [03_Delta_Lake_and_Lakehouse.md:426].

Q14: Scenario β€” You accidentally deleted critical data 3 days ago. How do you recover?

Answer:

sql
-- Step 1: Check history to find the version before the delete
DESCRIBE HISTORY customers;
-- Let's say the DELETE was version 42, so we want version 41

-- Step 2: Option A β€” Restore entire table (fastest, simplest)
RESTORE TABLE customers TO VERSION AS OF 41;

-- Step 2: Option B β€” Selectively restore only deleted rows
INSERT INTO customers
SELECT * FROM customers VERSION AS OF 41
WHERE customer_id NOT IN (SELECT customer_id FROM customers);

-- Step 2: Option C β€” MERGE for surgical recovery
MERGE INTO customers AS target
USING customers VERSION AS OF 41 AS source
ON target.customer_id = source.customer_id
WHEN NOT MATCHED THEN INSERT *;

Time travel limits:

  • Default data retention: 7 days (delta.deletedFileRetentionDuration)
  • Default log retention: 30 days (delta.logRetentionDuration)

Interview Tip: "Option C (MERGE) is the best answer β€” it shows you understand MERGE + time travel together, and it's surgical (only inserts deleted rows, doesn't overwrite changes made after the delete)."

What NOT to Say: "I'll restore from a backup" β€” Delta HAS time travel built in. You don't need a separate backup system for this.

  • VACUUM removes old files β†’ breaks time travel for those versions

Answer First: A lakehouse migration should establish governed open storage first, move batch and streaming workloads by domain, then retire duplicated warehouse and ML paths after validation.

Memory Map: Scenario β€” Your company has a data lake on S3, a Snowflake warehouse, and a separate ML platform. The CEO wants to consolidate. How do you design the lakehouse migration -> governed open storage establishes the shared foundation -> batch streaming SQL and ML move in phases -> compatibility and reconciliation protect cutover -> lineage SLA and cost validate consolidation [03_Delta_Lake_and_Lakehouse.md:732].

Q25: Scenario β€” Your company has a data lake on S3, a Snowflake warehouse, and a separate ML platform. The CEO wants to consolidate. How do you design the lakehouse migration?

Answer:

πŸ—‚οΈPhase 1: Foundation (Weeks 1-4)
Set up Databricks workspace with Unity Catalog
Configure cloud storage (S3/ADLS) as external locations
Establish IAM roles / service principals
Define medallion architecture standards
Phase 2: Data Migration (Weeks 5-12)
Bronze: Point Auto Loader at existing S3 raw data
Silver: Migrate Snowflake transformation logic to Databricks SQL/PySpark
Gold: Recreate Snowflake aggregation tables as Delta tables
Validate data parity between old and new
Phase 3: ML Integration (Weeks 10-14)
Migrate ML feature engineering to Silver/Gold layers
Register models in MLflow (Unity Catalog)
Feature tables accessible to both BI and ML
Phase 4: BI Cutover (Weeks 13-16)
Connect BI tools to Databricks SQL Warehouse
Validate report parity
Decommission Snowflake
Key decisions:
- Use managed tables for new data, external tables for migrated data
- Liquid clustering instead of partitioning for new tables
- DLT for critical pipeline quality guarantees
- Unity Catalog for governance from day 1

Q14: Scenario β€” Someone accidentally deleted critical passenger data 3 days ago. How to recover?

Simple Explanation: This is a very common interview scenario. The answer uses time travel to see the data before the delete, then restore it.

sql
-- Step 1: Find the version BEFORE the accidental delete
DESCRIBE HISTORY dim_passenger;
-- Look at the output β€” find the DELETE operation
-- Let's say the DELETE was at version 42
-- So we want version 41 (the version just before the delete)

-- Step 2 Option A: FULL RESTORE β€” simplest, rolls back entire table
RESTORE TABLE dim_passenger TO VERSION AS OF 41;
-- This makes the table look exactly like version 41
-- But it's a new version (43), so nothing is lost β€” you can undo this too

-- Step 2 Option B: SELECTIVE RESTORE β€” only bring back deleted rows
-- Use MERGE to insert only the rows that are missing (were deleted)
MERGE INTO dim_passenger AS target                        -- Current table (missing rows)
USING dim_passenger VERSION AS OF 41 AS source            -- Old version (has all rows)
ON target.passenger_id = source.passenger_id              -- Match by business key
WHEN NOT MATCHED THEN INSERT *;
-- This only inserts rows that exist in old version but NOT in current table
-- i.e., exactly the rows that were deleted

Interview tip: Option B is the better answer β€” it shows you understand MERGE + time travel together, and it's more surgical (doesn't overwrite any changes that happened after the delete).

QUICK REVISION CHECKLIST β€” DAY 1

Test yourself β€” can you answer each in 2-3 minutes?

  • What is Delta Lake? What problem does it solve? (Q1)
  • What is the transaction log and how does it ensure ACID? (Q1)
  • What are checkpoint files and why are they needed? (Q2)
  • How does optimistic concurrency control work? (Q3)
  • What is data skipping and how do file-level statistics help? (Q4)
  • Can you write a MERGE with all 4 clauses (matched, not-matched, delete, not-matched-by-source)? (Q5)
  • How do you handle duplicate keys in source during MERGE? (Q6)
  • List 6 ways to optimize a slow MERGE. (Q7)
  • What is OPTIMIZE? What is VACUUM? What's the difference? (Q9)
  • What is Z-ORDER? What is Partitioning? What is Liquid Clustering? When to use each? (Q10)
  • What are Deletion Vectors and why are they useful? (Q11)
  • How does Time Travel work? How to recover deleted data? (Q13, Q14)
  • What is a Lakehouse? How is it different from Data Lake and Data Warehouse? (Q15)
  • What are 2-3 new features in Delta Lake 4.x? (Q17)
  • What is Predictive Optimization? (Q18)
  • What is the difference between Managed and External tables? (Q20)

🧠 FINAL REVISION β€” Day 1 Summary Card

πŸ“ Architecture Diagram
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    DAY 1: DELTA LAKE                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                             β”‚
β”‚  DELTA = Parquet files + Transaction Log (_delta_log/)      β”‚
β”‚  Transaction Log = JSON commits + Checkpoints (every 10)    β”‚
β”‚  ACID = Atomicity, Consistency, Isolation, Durability       β”‚
β”‚  Concurrency = Optimistic (assume no conflict, check later) β”‚
β”‚                                                             β”‚
│  MERGE = Match→Update, No Match→Insert ("MU-NI")           │
β”‚  ⚠️ Source must be deduplicated (ROW_NUMBER trick)           β”‚
β”‚  SCD2 merge_key trick = NULL key for new rows β†’ INSERT      β”‚
β”‚  6 optimizations = "FSCPZL"                                 β”‚
β”‚                                                             β”‚
β”‚  OPTIMIZE = compact small files (run daily)                 β”‚
β”‚  VACUUM = delete old files (run weekly, default 7 days)     β”‚
β”‚  Z-ORDER = sort by columns (OLD way)                        β”‚
β”‚  Liquid Clustering = auto-sort (NEW way, replaces Z+Part)   β”‚
β”‚  Deletion Vectors = mark rows deleted without rewriting     β”‚
β”‚                                                             β”‚
β”‚  Time Travel = VERSION AS OF / TIMESTAMP AS OF              β”‚
β”‚  RESTORE = undo mistakes (creates new version, safe)        β”‚
β”‚  ⚠️ Only works within VACUUM retention (default 7 days)     β”‚
β”‚                                                             β”‚
β”‚  Lakehouse = Lake PRICE + Warehouse FEATURES                β”‚
β”‚  New: Predictive Opt, Lakebase, Multi-table Tx, Variant    β”‚
β”‚                                                             β”‚
β”‚  TOP 5 THINGS TO SAY IN INTERVIEW:                          β”‚
β”‚  1. "Transaction log ensures ACID on cloud storage"         β”‚
β”‚  2. "MERGE with Liquid Clustering for fast upserts"         β”‚
β”‚  3. "Liquid Clustering replaces partitioning + Z-ORDER"     β”‚
β”‚  4. "VACUUM retention balances storage vs time travel"      β”‚
β”‚  5. "Predictive Optimization auto-manages file layout"      β”‚
β”‚                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
πŸ—ΊοΈ Memory Map
Study tip: Read this file TWICE:
  1. First pass (30 min): Read only 🧠 Memory Maps + ⚑ Direct Questions
  2. Second pass (30 min): Read πŸ”‘ Mid-Level Questions + ⚠️ Traps
  3. Before interview (15 min): Read ONLY the Final Revision Summary Card

SECTION 9: Delta Lake vs Apache Iceberg vs Apache Hudi

Answer First: Delta, Iceberg, and Hudi all add table metadata over open files, but their transaction models, engine ecosystems, maintenance behavior, and governance integrations differ.

Memory Map: Delta Lake, Apache Iceberg, and Apache Hudi. (The dreaded comparison question) -> transaction design differs across the three formats -> engine support determines interoperability -> update and ingestion patterns affect fit -> operational ownership and pilot results drive selection [DB_06_Delta_Lake_Advanced_Masterclass.md:1198].

Q22: Compare Delta Lake, Apache Iceberg, and Apache Hudi. (The dreaded comparison question)

Answer -- structure this as a table, then add nuance:

πŸ“ Architecture Diagram
Feature                  | Delta Lake          | Apache Iceberg       | Apache Hudi
─────────────────────────┼─────────────────────┼──────────────────────┼────────────────────
Created by               | Databricks (2019)   | Netflix (2017)       | Uber (2016)
Open Source              | Yes (Linux Fdn)     | Yes (Apache)         | Yes (Apache)
Primary ecosystem        | Spark/Databricks    | Engine-agnostic      | Spark-centric
Transaction log format   | JSON + Parquet      | JSON + Avro manifests| Timeline (Avro)
Storage format           | Parquet             | Parquet, ORC, Avro   | Parquet, HFile
ACID transactions        | Yes (OCC)           | Yes (OCC)            | Yes
Schema evolution         | Yes                 | Yes (best-in-class)  | Yes
Partition evolution       | No (must recreate)  | Yes (hidden parts)   | Limited
Time travel              | Yes (version/time)  | Yes (snapshots)      | Yes (timeline)
CDC support              | Change Data Feed    | Incremental reads    | Native CDC (MoR/CoW)
Streaming support        | Spark Structured    | Flink, Spark         | Spark, Flink
Multi-engine support     | Improving (UniForm) | Best (native)        | Good (Spark, Flink)
Copy-on-Write            | Yes                 | Yes                  | Yes (CoW table type)
Merge-on-Read            | Yes (via DVs)       | Yes (delete files)   | Yes (MoR table type)
Clustering               | Liquid Clustering   | Sort orders          | Clustering
Compaction               | OPTIMIZE            | Rewrite data files   | Compaction service
Catalog                  | Unity Catalog       | REST, Hive, Nessie   | Hive, AWS Glue
Vendor backing           | Databricks          | Apple, Netflix, AWS  | Uber, AWS, Onehouse
Cloud adoption           | Very high (Azure)   | Very high (AWS)      | Moderate

SECTION 10: Rapid-Fire Interview Questions with Traps

Answer First: Rapid-fire Delta questions test whether a candidate can pair a default or capability with its configuration boundary and failure consequence. Answer each independently instead of carrying the previous question’s value into the next one.

Memory Map: Rapid-fire "gotcha" questions interviewers love -> rapid prompt isolates one Delta invariant -> concise response names controlling log state -> counterexample exposes common trap -> version or file evidence verifies claim [DB_06_Delta_Lake_Advanced_Masterclass.md:1609].

Q34: Rapid-fire "gotcha" questions interviewers love

Q"What is the default file size target for OPTIMIZE?"

~1 GB (configurable via spark.databricks.delta.optimize.maxFileSize in bytes, default 1073741824)

Q"What is the default VACUUM retention period?"

168 hours (7 days). Configured via delta.deletedFileRetentionDuration.

Q"What is the default checkpoint interval?"

Every 10 commits. Configured via delta.checkpointInterval.

Q"Can you time travel to a version that has been VACUUMed?"

No. The data files are physically deleted. You get FileNotFoundException.

Q"Does OPTIMIZE block reads?"

No. Reads use snapshot isolation and continue reading from their snapshot. OPTIMIZE creates new files in a new commit -- reads see either the old version or the new version, never an inconsistent state.

Q"Does VACUUM block writes?"

No. VACUUM only deletes files that are no longer referenced. Active writes create new files that VACUUM does not touch (they are too new).

Q"What is the _last_checkpoint file?"

A small JSON file that points to the most recent checkpoint file. Delta reads this first to avoid scanning the entire _delta_log/ directory.

Q"How does Delta Lake handle NULL values in Z-ORDER columns?"

NULLs are treated as a special value and clustered together. This is actually beneficial -- files with NULLs are segregated, so queries filtering for non-NULL values can skip those files entirely.

Q"What is the maximum number of columns Delta can store statistics for?"

32 by default (delta.dataSkippingNumIndexedCols). Only the first 32 columns get min/max stats for data skipping. Move your most-filtered columns to the front of the schema, or increase this number.

Q"Can you convert a Parquet table to Delta without copying data?"

Yes! CONVERT TO DELTA parquet.'path/to/table' -- this creates the _delta_log/ on top of existing Parquet files. No data copy. But you should run OPTIMIZE afterward for best performance.

Q"What happens to Delta tables when the cluster is terminated?"

Nothing. Delta tables are stored on durable cloud storage (S3/ADLS/GCS). The cluster is just compute. Tables persist independently of any cluster.

Q"Is Delta Lake open source?"

Yes. Delta Lake is open source under the Apache 2.0 license, governed by the Linux Foundation (Delta Lake project). However, some features (Predictive Optimization, Unity Catalog integration, serverless) are Databricks-proprietary.

QUICK REVISION CHECKLIST

Before your interview, make sure you can answer these from memory:

β–‘ Transaction log: JSON commits + Parquet checkpoints + _last_checkpoint
β–‘ ACID: how each letter is implemented (atomic commit, schema enforcement, snapshot isolation, cloud storage durability)
β–‘ Optimistic concurrency: read version -> work -> try commit -> conflict check -> retry
β–‘ OPTIMIZE: bin-packing vs Z-ORDER, incremental Liquid Clustering
β–‘ VACUUM: 7-day default, risks with 0 hours, interaction with long queries
β–‘ Deletion Vectors: RoaringBitmap, soft deletes, purged by OPTIMIZE
β–‘ Change Data Feed: 4 change types, not retroactive, storage overhead
β–‘ UniForm: write Delta + generate Iceberg/Hudi metadata, one-way sync
β–‘ Predictive Optimization: auto OPTIMIZE/VACUUM, requires Unity Catalog
β–‘ Small file problem: 6 root causes, 7 solutions
β–‘ Delta vs Iceberg: partition evolution vs Liquid Clustering, multi-engine support
β–‘ Protocol versions: irreversible upgrades, reader/writer compatibility
β–‘ Schema enforcement vs evolution: mergeSchema (additive) vs overwriteSchema (destructive)
β–‘ MERGE: dedup source, partition pruning, Z-ORDER on merge key, broadcast small source
β–‘ Time travel: version-based, timestamp-based, VACUUM destroys old versions
β–‘ Liquid Clustering: Hilbert curve, incremental, replaces partitioning + Z-ORDER
β–‘ Shallow vs Deep Clone: shallow references source files, VACUUM can break it
β–‘ WAP pattern: write to branch, audit, publish
β–‘ Row Tracking: stable row IDs, enables precise DVs and CDC
β–‘ Type Widening: safe type promotions without rewrite

Q19 β€” Scenario: Accidental DELETE β€” How to recover?

Question: "Someone ran DELETE FROM customers WHERE region = 'APAC' by mistake. 2 million rows deleted. How do you recover?"

sql
-- Step 1: Find the version BEFORE the accidental delete
DESCRIBE HISTORY customers;
-- Look for the DELETE operation β€” say it was at version 42
-- We want version 41 (just before the delete)

-- Option A: FULL RESTORE (simple but blunt)
RESTORE TABLE customers TO VERSION AS OF 41;
-- Table looks exactly like version 41
-- BUT: any legitimate changes between 42 and now are also lost!

-- Option B: SELECTIVE RESTORE (surgical β€” better answer!)
MERGE INTO customers AS target
USING customers VERSION AS OF 41 AS source
ON target.customer_id = source.customer_id
WHEN NOT MATCHED THEN INSERT *;
-- Only inserts rows that were deleted (exist in v41 but not current)
-- Preserves any legitimate changes that happened after the delete

Interview Tip: "Option B is the better answer β€” it shows you understand MERGE + time travel together, and it's surgical (doesn't overwrite changes made after the accidental delete)."

SECTION 8: DELTA LAKE vs COMPETITORS

Q27 β€” Delta Lake vs Apache Iceberg vs Apache Hudi

Question: "How does Delta Lake compare to Iceberg and Hudi? Why would you choose one over another?"

AspectDelta LakeApache IcebergApache Hudi
Created byDatabricksNetflix β†’ ApacheUber β†’ Apache
Best withDatabricks, SparkSnowflake, Trino, Spark, FlinkSpark, Flink
ACIDYesYesYes
Time TravelYesYesYes (limited)
MERGE performanceExcellent (Photon)GoodGood (MoR tables)
StreamingExcellentGoodExcellent
Schema EvolutionGoodExcellent (best)Good
Partition EvolutionVia Liquid ClusteringNative (hidden partitioning)Manual
Multi-engine supportUniForm bridges gapBest (truly engine-agnostic)Good
Community/EcosystemLarge (Databricks-led)Fastest growingSmaller
Vendor independenceDatabricks-optimizedMost vendor-neutralLess common

When to choose each:

🧠 Memory Map
Delta Lake→You're on Databricks (obvious choice — best integration)
Iceberg→Multi-engine environment (Snowflake + Spark + Trino)
Hudi→Heavy streaming upsert workloads (Uber-style CDC)

Interview Tip: "Don't bash other formats. Say: 'Delta is the natural choice on Databricks because of deep integration with Photon, Unity Catalog, and Predictive Optimization. But UniForm lets us expose tables as Iceberg for teams using Snowflake.'"

SECTION 9: PRODUCTION SCENARIOS & GOTCHAS

Answer First: A real-time order pipeline appends ordered source events to Bronze, validates and deduplicates them in Silver, and publishes current and historical views to Gold. Checkpoints, source sequence keys, and idempotent writes make replay safe.

Memory Map: Scenario: Design a Delta Lake pipeline for a real-time order system -> order events arrive with stable identities -> watermark and checkpoint bound streaming state -> idempotent merge updates Delta orders -> latency and count reconciliation verify service [Delta_01_Complete_Guide.md:1366].

Q30 β€” Scenario: Design a Delta Lake pipeline for a real-time order system

Question: "Design a medallion architecture for an e-commerce order system using Delta Lake."

πŸ“ Architecture Diagram
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   BRONZE    β”‚     β”‚   SILVER    β”‚     β”‚    GOLD     β”‚
β”‚  Raw data   │────→│  Cleaned    │────→│ Aggregated  β”‚
β”‚  as-is      β”‚     β”‚  validated  β”‚     β”‚ business    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

BRONZE LAYER:
  - Auto Loader ingests raw JSON/CSV from cloud storage
  - CLUSTER BY (ingest_date)
  - Schema: raw_payload STRING, source STRING, ingest_timestamp TIMESTAMP
  - enableChangeDataFeed = true (for Silver to read changes)

SILVER LAYER:
  - Reads BRONZE changes via CDF (incremental!)
  - MERGE to deduplicate and validate
  - CLUSTER BY (order_date, customer_id)
  - Schema enforcement + data quality checks (expectations)
  - enableChangeDataFeed = true (for Gold to read changes)

GOLD LAYER:
  - Reads SILVER changes via CDF
  - Aggregations: daily revenue, customer metrics, product performance
  - Optimized for BI queries (pre-computed)
  - CLUSTER BY (report_date)

Key decisions in this design:

  • Liquid Clustering everywhere (not partitioning)
  • CDF enabled at each layer for incremental processing
  • Managed tables for Predictive Optimization
  • Auto Loader for exactly-once ingestion
  • MERGE for deduplication at Silver layer

Answer First: Production Delta failures usually trace to ambiguous MERGE keys, incompatible concurrent writes, schema drift, unsafe retention, or fragmented files. Diagnose the committed version and operation metrics before changing table state.

Memory Map: Common production gotchas with Delta Lake -> production checklist starts with log and file health -> retention and concurrency policies constrain maintenance -> monitoring catches drift before failure -> recovery rehearsal proves safeguards [Delta_01_Complete_Guide.md:1406].

Q31 β€” Common production gotchas with Delta Lake

Question: "What are the most common issues you've seen with Delta Lake in production?"

GotchaSymptomFix
Small file problemQueries slow, too many Spark tasksOPTIMIZE + autoOptimize properties
VACUUM too aggressiveTime travel broken, readers fail with FileNotFoundExceptionKeep default 7-day retention, never RETAIN 0
MERGE without partition filterMERGE takes hours on large tablesAdd partition column to ON clause
Over-partitioningThousands of tiny files per partitionSwitch to Liquid Clustering
Schema mismatchWrite fails with schema errorUse mergeSchema or overwriteSchema as appropriate
Concurrent write conflictsConcurrentModificationExceptionPartition writes so pipelines touch different files
Stale statisticsData skipping not working, queries slowANALYZE TABLE or enable Predictive Optimization
Z-ORDER on too many columnsNo improvement in query speedMax 4 columns, choose most filtered ones
Not using PhotonMERGE and queries 3-5x slower than neededSwitch cluster to Photon runtime
Streaming + OPTIMIZE conflictOPTIMIZE blocks streaming writesUse autoCompact instead of manual OPTIMIZE

SECTION 10: NEW FEATURES (2025-2026)

QUICK REVISION CHECKLIST

Can you answer each in 2-3 minutes?

  • Why do we need Delta Lake over plain Parquet? (Q01)
  • List 8 key features of Delta Lake (Q02)
  • How does Delta ensure ACID? What happens if a write fails midway? (Q03)
  • What's inside a transaction log commit? (Q04)
  • What are checkpoints and why are they critical? (Q05)
  • How does optimistic concurrency work? (Q06)
  • Write a MERGE from memory with all 4 clauses (Q07)
  • How to handle duplicate keys in MERGE? (Q08)
  • List 6 ways to make MERGE faster (Q09)
  • What is OPTIMIZE? Good and bad? (Q11)
  • What is VACUUM? Risks of RETAIN 0? (Q12)
  • How to fix the small file problem? (Q13)
  • Partitioning vs Z-ORDER vs Liquid Clustering β€” when to use each? (Q14-Q16)
  • How does data skipping work? (Q17)
  • How does Time Travel work? Recover from accidental delete? (Q18-Q19)
  • What is CDF? How does it help incremental ETL? (Q21)
  • What are Deletion Vectors? Trade-offs? (Q22)
  • Delta vs Iceberg vs Hudi β€” when to choose each? (Q27)
  • Design a medallion architecture with Delta (Q30)
  • Name 3 new features in Delta 4.x (Q32)

APPENDIX: LEARN BY WATCHING + DOING

Answer First: Run this lab in Databricks Free Edition or a regular workspace. Free Edition is serverless-only and limited to one SQL warehouse, so keep the exercise within its supported compute and quota limits.

Memory Map: LAB 1 β€” Watch the Transaction Log Grow in Real Time -> disposable table begins at version zero -> each mutation publishes one later commit -> filesystem inspection connects actions to snapshots -> cleanup leaves no shared lab state [Delta_01_Complete_Guide.md:1478].

LAB 1 β€” Watch the Transaction Log Grow in Real Time

Goal: See EXACTLY what Delta writes on every operation. No magic. Where: Databricks Free Edition or a regular workspace. Free Edition is serverless-only, permits one SQL warehouse, and has documented quotas; review the current Free Edition limitations before running the lab. Time: 10 minutes

GOTCHAS β€” The Weird Errors You'll Hit (and How to Fix Them)

Answer First: Why it happens: Your source DataFrame has a column the target table doesn't have (or different type).

Memory Map: Schema Mismatch β€” "A schema mismatch detected when writing" -> incoming fields conflict with the table contract -> enforcement blocks the incompatible commit -> explicit evolution handles supported additions -> schema comparison identifies the required correction [Delta_01_Complete_Guide.md:1906].

Gotcha 2: Schema Mismatch β€” "A schema mismatch detected when writing"

Error: AnalysisException: A schema mismatch detected when writing to the Delta table.
To enable schema migration using DataFrameWriter or DataStreamWriter,
please set: .option("mergeSchema", "true")

Why it happens: Your source DataFrame has a column the target table doesn't have (or different type).

Fix:

# Option 1: allow new columns to be added automatically
df.write.format("delta") \
.option("mergeSchema", "true") \ # ← THIS solves it
.mode("append") \
.save(path)
# Option 2 (for MERGE operations):
spark.conf.set("spark.databricks.delta.schema.autoMerge.enabled", "true")

Interview trap: "How is this different from schema ENFORCEMENT?" β†’ Enforcement REJECTS bad writes by default. mergeSchema=true opts in to schema EVOLUTION.

Answer First: Why it happens: Two jobs MERGED into the same partition at the same time. Both added files β†’ conflict at commit time.

Memory Map: ConcurrentAppendException -> concurrent writer adds files in the same read domain -> optimistic validation detects conflicting append -> narrower predicates or serialized orchestration reduce collision -> retried commit confirms resolution [Delta_01_Complete_Guide.md:1933].

Gotcha 3: ConcurrentAppendException

Error: ConcurrentAppendException: Files were added to partition [date=2026-04-05]
by a concurrent update. Please try the operation again.

Why it happens: Two jobs MERGED into the same partition at the same time. Both added files β†’ conflict at commit time.

Fix:

python β€” editable
# Add a filter in your MERGE condition to restrict partitions
dt.alias("t").merge(
    source.alias("s"),
    "t.date = '2026-04-05' AND t.id = s.id"   # ← partition filter narrows conflict scope
).whenMatchedUpdate(...).execute()

# Or: retry with exponential backoff
import time
for attempt in range(3):
    try:
        # your merge
        break
    except Exception as e:
        if "ConcurrentAppend" in str(e):
            time.sleep(2 ** attempt)
        else:
            raise

Answer First: A missing Delta table usually means the job resolved a different path or catalog object, lacks storage permission, or points at a folder without a valid transaction log. Verify the fully qualified name, location, credentials, and _delta_log before changing data.

Memory Map: "Delta Table not found" when path looks correct -> path inspection distinguishes data directory from Delta root -> transaction-log presence establishes table identity -> catalog registration resolves name access -> filesystem and metadata checks verify repair [Delta_01_Complete_Guide.md:2015].

Gotcha 6: "Delta Table not found" when path looks correct

Error: Path does not exist: /mnt/data/my_table
(or: _delta_log does not exist)

Why it happens: Either:

  • You wrote as Parquet but are trying to read as Delta
  • You deleted _delta_log/ manually (DON'T)
  • Path typo

Fix:

python β€” editable
# Always verify before reading
print(dbutils.fs.ls(path))                      # check path exists
print(dbutils.fs.ls(f"{path}/_delta_log"))      # check _delta_log exists

# Convert plain Parquet to Delta if needed:
from delta.tables import DeltaTable
DeltaTable.convertToDelta(spark, f"parquet.`{path}`")

MOCK INTERVIEW β€” Live Walkthrough

⚠️ Common Trap
Read this like watching an interview. See the bad answer β†’ good answer β†’ follow-up trap.

Answer First: Diagnose the 2 TB table from operation metrics and query scans: first bound the MERGE match domain, deduplicate source keys, inspect file count and clustering, then measure whether the revised commit reduces files and bytes scanned.

Memory Map: Scenario: Senior Data Engineer Round (30 min Delta deep-dive) -> senior discussion begins with transaction invariants -> design scenario tests concurrency and recovery -> performance trade-off connects layout to workload -> evidence-backed follow-ups prove ownership [Delta_01_Complete_Guide.md:2044].

Scenario: Senior Data Engineer Round (30 min Delta deep-dive)

INTERVIEWER: "We have a 2 TB Delta table being updated every hour by a MERGE job. Lately, queries are getting slower and slower. Walk me through how you'd debug this."

❌ BAD ANSWER:

"I would run OPTIMIZE to fix it."

(Too short. No reasoning. No investigation. Red flag.)

βœ… GOOD ANSWER:

"Three things come to mind, and I'd investigate in order. First, I'd run DESCRIBE HISTORY and DESCRIBE DETAIL to check the file count and size distribution β€” if we have thousands of small files from hourly MERGEs, that's small-file problem and OPTIMIZE would help. Second, I'd check if any column has high-cardinality filters in queries β€” if yes, we might need Z-ORDER on that column for data skipping. Third, I'd check the MERGE condition itself β€” if it's not partition-pruning, every MERGE is rewriting files across the whole table. Let me describe how I'd verify each..."

FOLLOW-UP TRAP: (interviewer nods) "Good. Now β€” OPTIMIZE is running but taking 4 hours and blocking the pipeline. What do you do?"

❌ BAD ANSWER: "Run it less often."

βœ… GOOD ANSWER:

"I'd do three things. First, run OPTIMIZE with a partition filter so it only compacts recent partitions β€” OPTIMIZE table WHERE date >= current_date() - 7. Second, enable auto-compaction at the table level so small files get compacted incrementally during writes, reducing how much OPTIMIZE has to do. Third, if we're on a recent Databricks runtime, I'd consider switching from partitioning + Z-ORDER to Liquid Clustering, which handles incremental clustering automatically without blocking writes."

INTERVIEWER: "A user accidentally ran DELETE FROM customers WHERE status = 'active' β€” deleted 4 million rows. It's been 2 hours. Can you recover?"

❌ BAD ANSWER: "No, the data is gone."

❌ BAD ANSWER 2: "Yes, just restore from backup." (This is Delta β€” there's no separate backup needed)

βœ… GOOD ANSWER:

"Yes, absolutely β€” Delta's time travel makes this easy. Two hours ago is well within default retention. I'd run DESCRIBE HISTORY customers to find the version BEFORE the DELETE, then either: option A β€” RESTORE TABLE customers TO VERSION AS OF to restore the whole table, or option B β€” if we want to preserve changes AFTER the bad DELETE, do INSERT INTO customers SELECT * FROM customers VERSION AS OF WHERE status = 'active' to re-insert just the deleted rows. Option B is safer in production because it doesn't rewind other legitimate changes."

FOLLOW-UP TRAP: "What if it had been 10 days ago, not 2 hours?"

βœ… GOOD ANSWER:

"Then it depends on retention settings. Default is 7 days for data file retention, 30 days for log retention. If VACUUM ran with default retention, the old data files would already be deleted from storage β€” even though the transaction log would still reference them. Time Travel would fail with FileNotFoundException. The prevention is setting delta.deletedFileRetentionDuration to something higher like 30 days for critical tables, or taking scheduled shallow clones as point-in-time snapshots."

INTERVIEWER: "Explain what happens on disk when you run UPDATE customers SET email = LOWER(email)."

❌ BAD ANSWER: "It updates the rows."

βœ… GOOD ANSWER:

πŸ“ Note
"Delta files are immutable, so UPDATE works in three steps. First, Delta identifies which Parquet files contain rows matching the filter β€” in this case, ALL files since there's no WHERE clause. Second, for each affected file, it reads the file, applies LOWER to email for every row, and writes a NEW Parquet file. Third, it writes ONE commit JSON to _delta_log that has remove entries for all the old files and add entries for all the new files. The old files stay on disk until VACUUM runs, which is what enables time travel back to the pre-update version. Note: with Deletion Vectors enabled, UPDATE can be faster because it writes a small vector file marking which rows changed, avoiding the full file rewrite."

INTERVIEWER: "We have Job A doing hourly MERGE into table X, and Job B doing hourly OPTIMIZE on table X. They started conflicting. Why, and how do you fix it?"

❌ BAD ANSWER: "Run them at different times."

βœ… GOOD ANSWER:

"This is an optimistic concurrency conflict. OPTIMIZE marks a set of files as 'removed' and adds one compacted file. If MERGE reads those same files and also tries to add/remove files in an overlapping set, Delta throws ConcurrentAppendException at commit time. Two fixes: First, OPTIMIZE is idempotent and retryable β€” wrap it with retry logic so a conflict just triggers a retry after the MERGE finishes. Second, partition the table so OPTIMIZE targets OLD partitions and MERGE targets the CURRENT partition β€” e.g., OPTIMIZE WHERE date < current_date(). That way they work on disjoint file sets and never conflict."

FINAL READINESS CHECK

If you can do all of these, you're video-free:

  • Run LAB 1 end-to-end without looking at the answers
  • Run LAB 2 and explain each MERGE clause from memory
  • Run LAB 3 and explain why you see 11 files after OPTIMIZE (not 1)
  • Draw the Optimistic Concurrency animation on a whiteboard
  • Draw the Z-ORDER animation on a whiteboard
  • Name all 6 Gotchas and how to fix each
  • Answer all 4 mock interview questions using the "good answer" framework

If yes β†’ you don't need a single video. You're ready.

Routed Delta question-bank section indexes

L3 β€” Scenario-Based Questions

  1. MERGE Optimization: Your MERGE INTO statement takes 45 minutes on a 2 TB Delta table. Walk me through how you would diagnose and optimize this.

    ⚠️ Common Trap
    Approach: (1) DESCRIBE DETAIL to check numFiles and size, (2) Spark UI to find bottleneck stage (shuffle vs scan), (3) Check if partition column is in ON clause (partition pruning). Key decisions: OPTIMIZE the target first, add partition column to ON clause, Z-ORDER on merge key, filter source to only changed rows, enable Photon. Code: OPTIMIZE target_table ZORDER BY (merge_key); MERGE INTO target USING source ON target.partition_col = source.partition_col AND target.id = source.id. Traps: Don't MERGE without a partition filter β€” full table scan. Don't skip deduplication of source β€” duplicate keys cause incorrect matches.
  2. Transaction Log Corruption: A developer accidentally ran VACUUM with 0-hour retention and now Time Travel queries fail. What happened and how do you recover?

    ⚠️ Common Trap
    Approach: VACUUM deleted all Parquet files not in the current version. Time Travel needs those old files. Key decisions: (1) Check if cloud storage has soft-delete/versioning enabled on ADLS Gen2 (can recover deleted files within retention), (2) If not, check for backups, (3) Current version is intact β€” only history is lost, (4) Prevent recurrence: cluster policy blocking 0-hour retention. Traps: Don't assume the table is corrupted β€” the current version is fine. The issue is only with historical versions.
  3. Small File Problem: Your Bronze table has 50,000 small Parquet files (avg 2 MB each). How do you fix this and prevent it from recurring?

    ⚠️ Common Trap
    Approach: (1) Immediate: OPTIMIZE bronze_table to compact to ~1 GB files, then VACUUM. (2) Prevent: enable autoOptimize.optimizeWrite = true (coalesces during write), autoOptimize.autoCompact = true (background compaction). (3) Long-term: consider Liquid Clustering for automatic maintenance. Code: ALTER TABLE bronze_table SET TBLPROPERTIES ('delta.autoOptimize.optimizeWrite' = 'true', 'delta.autoOptimize.autoCompact' = 'true'). Traps: Don't just run OPTIMIZE once β€” the problem will recur without auto-compaction enabled.
  4. Concurrent Writes: Two Databricks jobs write to the same Delta table simultaneously and one fails with a ConcurrentAppendException. Explain why and how you fix it.

    ⚠️ Common Trap
    Approach: Both jobs read the table at version N, both try to add files. If they modify overlapping file sets, the second commit detects a conflict. Key decisions: (1) Partition writes by different dimensions (job A writes partition A, job B writes partition B β€” disjoint writes succeed), (2) Use Write-Audit-Publish (WAP) pattern, (3) Serialize writes if unavoidable. Traps: Don't disable conflict detection. Don't ignore the error and retry blindly β€” understand why files overlap.
  5. Z-ORDER Strategy: You have a 10 TB Delta table queried by country, date, and customer_id. Design the partitioning and Z-ORDER strategy.

    ⚠️ Common Trap
    Approach: Partition by date (low cardinality, queries almost always filter by date, each partition is large). Z-ORDER by country, customer_id within each partition. Key decisions: Don't partition by customer_id (too many partitions, tiny files). Consider Liquid Clustering as alternative: CLUSTER BY (date, country, customer_id) β€” simpler, incremental, no separate OPTIMIZE needed. Traps: Over-partitioning by country AND date creates too many small partitions if some countries have little data.
  6. Liquid Clustering Migration: Your team wants to migrate from Z-ORDER to Liquid Clustering on a production table. What is your migration plan? Any risks?

    ⚠️ Common Trap
    Approach: (1) Test on staging with production-scale data copy, (2) ALTER TABLE table CLUSTER BY (col1, col2) β€” this changes the metadata only, (3) Run OPTIMIZE to trigger incremental clustering on existing data, (4) Monitor query performance before/after. Key decisions: Can change clustering keys without full rewrite. Existing data reorganized incrementally by subsequent OPTIMIZE runs. Traps: You must remove existing partitioning first if converting from partitioned+Z-ORDER to Liquid Clustering. Cannot have both partitioning and Liquid Clustering.
  7. Schema Evolution Crisis: A source system added 5 new columns overnight and your streaming pipeline failed. How do you design for schema evolution in Auto Loader + Delta Lake?

    ⚠️ Common Trap
    Approach: (1) Immediate fix: restart with cloudFiles.schemaEvolutionMode = "addNewColumns", (2) Long-term: enable rescue mode (cloudFiles.schemaEvolutionMode = "rescue") so unknown columns go to _rescued_data column instead of failing, (3) Add schema drift monitoring. Code: spark.readStream.format("cloudFiles").option("cloudFiles.schemaEvolutionMode", "addNewColumns").option("mergeSchema", "true"). Traps: Don't use failOnNewColumns unless you want strict schema control (it will fail on every source change).
  8. Time Travel for Audit: Your compliance team needs to prove what data looked like on a specific date 30 days ago. How do you implement this with Delta Lake Time Travel? What are the limitations?

    ⚠️ Common Trap
    Approach: SELECT * FROM table TIMESTAMP AS OF '2026-02-25'. Key decisions: Default VACUUM retention is 7 days β€” extend to >30 days for compliance: ALTER TABLE SET TBLPROPERTIES ('delta.deletedFileRetentionDuration' = 'interval 90 days'). Storage cost increases with longer retention. Traps: If VACUUM already ran with default retention, versions >7 days old are gone. Set retention BEFORE you need it. Also note: Time Travel queries older versions, which consume more storage.
  9. VACUUM vs Storage Costs: Your Delta table consumes 5x the expected storage due to retained old versions. Design a VACUUM strategy that balances cost vs Time Travel needs.

    ⚠️ Common Trap
    Approach: (1) Identify which tables need long retention (audit/compliance) vs short retention (intermediate ETL tables), (2) Set tiered retention: Gold compliance tables = 90 days, Silver = 14 days, Bronze = 7 days, temp tables = 1 day. (3) Schedule VACUUM as a daily maintenance job. (4) Enable Predictive Optimization for UC managed tables. Traps: Don't set retention too short β€” active long-running queries can fail with FileNotFoundException if VACUUM deletes files mid-read.
  10. CDC with Delta CDF: Design a pipeline where downstream consumers only process changed records from a Silver Delta table. How do you use Change Data Feed?

    ⚠️ Common Trap
    Approach: (1) Enable CDF on source: ALTER TABLE SET TBLPROPERTIES ('delta.enableChangeDataFeed' = 'true'), (2) Downstream reads changes: spark.readStream.option("readChangeFeed", "true").table("silver_table"), (3) Filter by _change_type (insert, update_postimage, delete), (4) Apply changes to downstream target. Traps: CDF only captures changes AFTER it's enabled β€” no retroactive history. The _change_type column is metadata, not physical data β€” don't try to write it to the target.
  11. Table Restore Scenario: A bad ETL job corrupted your Gold table at 3 AM. It is now 9 AM and 6 versions have been written since. Walk through the recovery process.

    ⚠️ Common Trap
    Approach: (1) DESCRIBE HISTORY gold_table to find the last good version (version before the corruption), (2) RESTORE TABLE gold_table TO VERSION AS OF , (3) Verify data quality on the restored version, (4) Re-run the ETL pipeline from the corrected source. Key decisions: RESTORE creates a new commit (doesn't rewrite history). Downstream consumers may need to be notified. Traps: Don't try manual DELETE + INSERT β€” RESTORE is atomic and faster. Don't forget to fix the root cause in the ETL code.
  12. Partition Evolution: Your table was partitioned by year/month/day but queries now filter primarily by region. How do you restructure without downtime?

    ⚠️ Common Trap
    Approach: (1) Create new table with CLUSTER BY (region, date) using Liquid Clustering, (2) INSERT INTO new_table SELECT * FROM old_table, (3) Validate row counts and data quality, (4) Rename tables atomically, (5) Update downstream consumers. Key decisions: Liquid Clustering is preferred over re-partitioning because clustering keys can be changed later without data rewrite. Traps: Cannot change partition columns on an existing table β€” must create a new table. Don't forget to copy table properties and grants.
  13. MERGE with SCD Type 2: Implement a MERGE strategy for SCD Type 2 on a customer dimension table where you need to close old records and insert new ones atomically.

    ⚠️ Common Trap
    Approach: Use the merge_key trick: source adds a merge_key that equals id for updated rows and NULL for new inserts of the old-record-close. MERGE ON target.id = source.merge_key AND target.is_current = true. MATCHED + changed β†’ update end_date, is_current = false. NOT MATCHED β†’ insert new current record. Code snippet: Pre-process source to generate two rows per change (close row + new row). Traps: Always deduplicate source before MERGE. Always filter target on is_current = true to avoid matching historical records.
  14. Delta Sharing: An external partner needs read access to a subset of your Delta table. How do you implement this securely using Delta Sharing?

    ⚠️ Common Trap
    Approach: (1) Create a share: CREATE SHARE partner_share, (2) Add filtered table: ALTER SHARE partner_share ADD TABLE gold_table PARTITION (partner_id = 'PARTNER_A'), (3) Create recipient: CREATE RECIPIENT partner_a, (4) Grant share: GRANT SELECT ON SHARE partner_share TO RECIPIENT partner_a. Key decisions: Partners read data in-place β€” no data copying. Row-level filtering via partition or WHERE clause. Column-level filtering via views. Traps: Delta Sharing is read-only. Cannot share data that lives outside Unity Catalog.
  15. Deletion Vectors in Production: After enabling Deletion Vectors, read performance on certain queries degraded. Explain why and how you would resolve this.

    ⚠️ Common Trap
    Approach: Deletion Vectors defer the physical rewrite β€” reads must merge the bitmap with the data at read time (Merge-on-Read). If many deletions accumulate without compaction, reads slow down. Fix: Run OPTIMIZE to compact and purge deletion vectors. Enable periodic OPTIMIZE or Predictive Optimization. Traps: Don't disable Deletion Vectors β€” the write performance benefit is significant. Instead, compact more frequently to keep read overhead low.
Intermediate

Delta Lake Interview Questions

#

Delta Lake Interview Questions

Use this page as a prompt deck. It intentionally contains source wording and exact owner links only; the complete explanations, code, caveats, and labs remain with their canonical concept owners.

Answer pattern

  1. Give the direct answer first.
  2. Explain the mechanism or data/control path.
  3. Name the production trade-off or trap.
  4. Close with the evidence that would verify the claim.

Canonical Q-DLT index

Q-DLT-001: What is the Delta Lake transaction log (_delta_log)? Explain how it ensures ACID transactions.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L26.

Q-DLT-002: What are checkpoint files? Why are they critical?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L69.

Q-DLT-003: Explain optimistic concurrency control in Delta Lake. What happens when two writers conflict?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L89.

Q-DLT-004: Explain file-level statistics and data skipping.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L122.

Q-DLT-005: What is the difference between OPTIMIZE and VACUUM?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L149.

Q-DLT-006: Can you run VACUUM with retention of 0 hours? What are the risks?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L186.

Q-DLT-007: What happens if a write fails midway in Delta Lake?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L207.

Q-DLT-008: Explain the MERGE INTO syntax. Write a basic upsert.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L223.

Q-DLT-009: How do you handle duplicate keys in the source during MERGE?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L271.

Q-DLT-010: Design a MERGE for an e-commerce order system (create, update, cancel).

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L299.

Q-DLT-011: What is the performance concern with MERGE? How do you optimize it?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L326.

Q-DLT-012: MERGE with schema evolution β€” how does it work?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L372.

Q-DLT-013: How does time travel work? Show all query methods.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L393.

Q-DLT-014: Scenario β€” You accidentally deleted critical data 3 days ago. How do you recover?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L426.

Q-DLT-015: What is Z-Ordering? How does it differ from partitioning?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L464.

Q-DLT-016: What is Liquid Clustering? How does it improve over Z-Ordering + Partitioning?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L499.

Q-DLT-017: What are Deletion Vectors? How do they improve UPDATE/DELETE performance?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L541.

Q-DLT-018: What are the most important Delta table properties?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L567.

Q-DLT-019: What is the difference between managed and external tables in Databricks?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L596.

Q-DLT-020: What are clone operations? Explain deep clone vs shallow clone.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L617.

Q-DLT-021: Explain schema evolution in Delta Lake. What are the options?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L641.

Q-DLT-022: What is a data lakehouse? How does it differ from data lake and data warehouse?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L677.

Q-DLT-023: What key technologies enable the lakehouse?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L700.

Q-DLT-024: What problems does the lakehouse solve?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L712.

Q-DLT-025: Scenario β€” Your company has a data lake on S3, a Snowflake warehouse, and a separate ML platform. The CEO wants to consolidate. How do you design the lakehouse migration?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L732.

Q-DLT-026: What is Delta Sharing? How does it work?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L767.

Q-DLT-027: Scenario β€” A Delta table has 10,000 small files (each <1 MB). Queries are slow. How do you fix this?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L792.

Q-DLT-028: What is the difference between DataFrame.write.mode("overwrite") and REPLACE TABLE in Delta?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L818.

Q-DLT-029: How do you implement write-audit-publish (WAP) pattern with Delta Lake?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/03_Delta_Lake_and_Lakehouse.md#L840.

Q-DLT-030: What is Delta Lake? And what is the transaction log?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L11.

Q-DLT-031: What are checkpoint files? Why are they important?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L59.

Q-DLT-032: What is Optimistic Concurrency Control?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L77.

Q-DLT-033: What is data skipping? How do file-level statistics work?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L108.

Q-DLT-034: What is MERGE? Basic syntax (upsert)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L140.

Q-DLT-035: What happens when source has duplicate keys? How to fix?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L187.

Q-DLT-036: How to make MERGE faster? (CRITICAL β€” commonly asked)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L216.

Q-DLT-037: What is schema evolution with MERGE?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L295.

Q-DLT-038: What is OPTIMIZE? What is VACUUM? What's the difference?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L322.

Q-DLT-039: What is Z-Ordering? What is Partitioning? What is Liquid Clustering?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L369.

Q-DLT-040: What are Deletion Vectors?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L463.

Q-DLT-041: What are the important Delta table properties?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L491.

Q-DLT-042: What is Time Travel? How to query old versions of a table?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L540.

Q-DLT-043: Scenario β€” Someone accidentally deleted critical passenger data 3 days ago. How to recover?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L576.

Q-DLT-044: What is a Data Lakehouse? How is it different from Data Lake and Data Warehouse?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L609.

Q-DLT-045: What technologies make the Lakehouse possible?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L638.

Q-DLT-046: What's new in Delta Lake 4.x? (Mention 2-3 in interview to show you're up to date)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L655.

Q-DLT-047: What is Predictive Optimization?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L673.

Q-DLT-048: What is Lakebase?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L692.

Q-DLT-049: What is the difference between Managed and External tables?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Delta_Lake_Deep_Dive.md#L715.

Q-DLT-050: What is Delta Lake?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L62.

Q-DLT-051: Where does Delta Lake store its metadata?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L65.

Q-DLT-052: What is the transaction log?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L68.

Q-DLT-053: What are the 4 properties of ACID?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L71.

Q-DLT-054: What format are Delta data files stored in?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L74.

Q-DLT-055: What is a checkpoint file?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L77.

Q-DLT-056: What is _last_checkpoint?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L80.

Q-DLT-057: What is snapshot isolation?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L83.

Q-DLT-058: What is optimistic concurrency control?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L86.

Q-DLT-059: When does optimistic concurrency FAIL (conflict)?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L89.

Q-DLT-060: What is data skipping?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L92.

Q-DLT-061: How many columns have statistics by default?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L95.

Q-DLT-062: How does Delta read a table? (Step by step)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L102.

Q-DLT-063: What happens internally when you INSERT data?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L109.

Q-DLT-064: What happens internally when you DELETE data?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L114.

Q-DLT-065: Does DELETE physically remove files?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L120.

Q-DLT-066: How does Delta handle concurrent writes? (Conflict resolution)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L123.

Q-DLT-067: What are the 4 types of actions in a commit JSON?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L130.

Q-DLT-068: What is MERGE INTO?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L167.

Q-DLT-069: What is an upsert?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L170.

Q-DLT-070: Can MERGE also DELETE rows?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L173.

Q-DLT-071: What happens if the source has duplicate keys in MERGE?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L176.

Q-DLT-072: How to fix duplicate keys in source?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L179.

Q-DLT-073: What is schema evolution in MERGE?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L190.

Q-DLT-074: How does MERGE work internally? (3 steps)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L199.

Q-DLT-075: Why is MERGE slow on large tables? How to optimize?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L204.

Q-DLT-076: What is the merge_key trick for SCD Type 2?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L212.

Q-DLT-077: What is OPTIMIZE?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L260.

Q-DLT-078: What is VACUUM?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L263.

Q-DLT-079: What is the default VACUUM retention?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L266.

Q-DLT-080: Can you VACUUM with 0 hours retention?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L269.

Q-DLT-081: What is Z-ORDER?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L276.

Q-DLT-082: What is Liquid Clustering?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L279.

Q-DLT-083: Can you use Liquid Clustering with partitioning?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L282.

Q-DLT-084: What are Deletion Vectors?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L285.

Q-DLT-085: Are Deletion Vectors enabled by default?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L288.

Q-DLT-086: OPTIMIZE vs VACUUM β€” what's the difference?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L295.

Q-DLT-087: Z-ORDER vs Liquid Clustering β€” when to use which?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L304.

Q-DLT-088: When should you NOT use partitioning?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L314.

Q-DLT-089: What is time travel in Delta Lake?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L357.

Q-DLT-090: How to query a specific version?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L360.

Q-DLT-091: How to see all versions of a table?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L363.

Q-DLT-092: How to recover from accidental deletion?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L366.

Q-DLT-093: When does time travel stop working?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L369.

Q-DLT-094: What is a Data Lakehouse?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L397.

Q-DLT-095: What makes Lakehouse possible? (4 technologies)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L400.

Q-DLT-096: Managed Table vs External Table?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L406.

Q-DLT-097: What is Predictive Optimization?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L434.

Q-DLT-098: What is Lakebase?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L437.

Q-DLT-099: What are Multi-table Transactions?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L440.

Q-DLT-100: What is Compatibility Mode?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L443.

Q-DLT-101: What is the Variant type?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_01_Quick_Recall.md#L446.

Q-DLT-102: What is Delta Lake and why was it created?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L12.

Q-DLT-103: What file format does Delta Lake use under the hood?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L15.

Q-DLT-104: What is the _delta_log directory and what does it contain?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L18.

Q-DLT-105: What are the four ACID properties and how does Delta Lake guarantee them?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L21.

Q-DLT-106: What is a checkpoint file in the Delta transaction log?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L24.

Q-DLT-107: What is schema enforcement in Delta Lake?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L27.

Q-DLT-108: What is schema evolution and how do you enable it?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L30.

Q-DLT-109: What is Time Travel in Delta Lake? How do you query an older version?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L33.

Q-DLT-110: What is the VACUUM command and what does it do?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L36.

Q-DLT-111: What is the default retention period for VACUUM?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L39.

Q-DLT-112: What does the OPTIMIZE command do?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L42.

Q-DLT-113: What is Z-ORDER and what problem does it solve?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L45.

Q-DLT-114: What are Deletion Vectors in Delta Lake?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L48.

Q-DLT-115: What is the difference between Delta Lake and Apache Parquet?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L51.

Q-DLT-116: What is the DESCRIBE HISTORY command used for?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L54.

Q-DLT-117: What is Change Data Feed (CDF) in Delta Lake?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L57.

Q-DLT-118: What is Liquid Clustering in Delta Lake?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L60.

Q-DLT-119: What is Predictive Optimization in Databricks?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L63.

Q-DLT-120: What are table constraints in Delta Lake (CHECK, NOT NULL)?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L66.

Q-DLT-121: What is the RESTORE command in Delta Lake?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L69.

Q-DLT-122: Explain the anatomy of a Delta Lake transaction β€” what happens when you write to a Delta table?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L74.

Q-DLT-123: How does optimistic concurrency control work in Delta Lake? What happens during write conflicts?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L77.

Q-DLT-124: Compare Z-ORDER vs Liquid Clustering β€” when would you use each?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L80.

Q-DLT-125: Explain data skipping in Delta Lake. How does it use min/max statistics?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L83.

Q-DLT-126: What is the difference between OPTIMIZE and VACUUM? Can you run them together?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L86.

Q-DLT-127: Explain how MERGE INTO works internally. What are the performance implications of a full table scan in MERGE?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L89.

Q-DLT-128: How does the Delta transaction log handle concurrent writes from multiple clusters?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L92.

Q-DLT-129: Compare schema enforcement vs schema evolution β€” give an example where each is appropriate.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L95.

Q-DLT-130: What happens if you run VACUUM with a retention of 0 hours? What are the risks?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L98.

Q-DLT-131: Explain the difference between OPTIMIZE WHERE and partition-level OPTIMIZE.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L101.

Q-DLT-132: How does Delta Lake handle small file compaction? What is the "small file problem"?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L104.

Q-DLT-133: Explain the difference between Copy-on-Write and Merge-on-Read in Delta Lake.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L107.

Q-DLT-134: How do Deletion Vectors improve UPDATE/DELETE performance compared to the traditional approach?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L110.

Q-DLT-135: What is the relationship between file statistics, data skipping, and Z-ORDER?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L113.

Q-DLT-136: Explain how Time Travel works internally β€” what is stored in each JSON commit file?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L116.

Q-DLT-137: Compare Change Data Feed (CDF) vs reading the transaction log directly for CDC.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L119.

Q-DLT-138: What is the difference between managed and external Delta tables?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L122.

Q-DLT-139: How does Liquid Clustering handle incremental clustering vs Z-ORDER which requires full rewrite?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L125.

Q-DLT-140: What are the trade-offs of over-partitioning a Delta table?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L128.

Q-DLT-141: Explain Delta Lake 4.x features: UniForm, Universal Format. Why do they matter?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_05_QUESTION_BANK_ALL_LEVELS.md#L131.

Q-DLT-142: What exactly does OPTIMIZE do internally? Walk me through the mechanics.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L26.

Q-DLT-143: When should you NOT run OPTIMIZE? Give me real scenarios where it causes problems.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L61.

Q-DLT-144: 1: During active write windows

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L65.

Q-DLT-145: 2: Append-only tables with time-partitioning

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L79.

Q-DLT-146: 3: Liquid Clustering tables

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L91.

Q-DLT-147: 4: Tables with Deletion Vectors enabled

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L101.

Q-DLT-148: 5: Very large tables with limited cluster resources

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L114.

Q-DLT-149: What is the difference between bin-packing OPTIMIZE and OPTIMIZE with ZORDER?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L133.

Q-DLT-150: Compare partitioning, Z-ORDER, and Liquid Clustering. When do you use each?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L172.

Q-DLT-151: How do you enable Liquid Clustering? Can you migrate an existing table?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L227.

Q-DLT-152: What is the Hilbert curve used in Liquid Clustering? How is it different from Z-ORDER?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L271.

Q-DLT-153: What are Deletion Vectors? How do they work internally?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L304.

Q-DLT-154: What are the performance implications of Deletion Vectors? When do they help vs hurt?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L367.

Q-DLT-155: How do Deletion Vectors interact with MERGE?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L434.

Q-DLT-156: What is Change Data Feed? How does it work internally?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L476.

Q-DLT-157: How do you read Change Data Feed? What are the query patterns?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L533.

Q-DLT-158: What are the gotchas with Change Data Feed?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L603.

Q-DLT-159: What is Delta UniForm? Why was it created?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L663.

Q-DLT-160: What are the limitations and gotchas of UniForm?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L713.

Q-DLT-161: What is Predictive Optimization? How does it work?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L766.

Q-DLT-162: What are the interview gotchas with Predictive Optimization?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L836.

Q-DLT-163: Explain the small file problem in Delta Lake. What causes it?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L878.

Q-DLT-164: Give me the complete playbook to fix the small file problem.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L945.

Q-DLT-165: What does VACUUM actually do? Walk me through the mechanics.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1037.

Q-DLT-166: What are the real-world VACUUM risks? Give me actual production incident scenarios.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1072.

Q-DLT-167: What are the essential VACUUM best practices?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1164.

Q-DLT-168: Compare Delta Lake, Apache Iceberg, and Apache Hudi. (The dreaded comparison question)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1198.

Q-DLT-169: When would you choose each format? Give me the decision framework.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1228.

Q-DLT-170: What is the Iceberg "hidden partitioning" advantage that interviewers always ask about?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1262.

Q-DLT-171: What is the difference between "delta.enableChangeDataFeed" and "readChangeFeed"?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1313.

Q-DLT-172: Can you run OPTIMIZE and VACUUM at the same time?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1332.

Q-DLT-173: What happens if a MERGE operation fails midway?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1351.

Q-DLT-174: What is the difference between Shallow Clone and Deep Clone?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1381.

Q-DLT-175: Explain the Delta Lake protocol -- reader and writer versions.

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1412.

Q-DLT-176: How does Delta Lake handle schema enforcement vs schema evolution?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1450.

Q-DLT-177: What is Write-Audit-Publish (WAP) pattern with Delta? How do you implement it?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1506.

Q-DLT-178: What is Row Tracking and what is it used for?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1540.

Q-DLT-179: What are Type Widening and Variant types in Delta Lake?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1563.

Q-DLT-180: Rapid-fire "gotcha" questions interviewers love

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/databricks/DB_06_Delta_Lake_Advanced_Masterclass.md#L1609.

Q-DLT-181: What is Delta Lake? Why do we need it?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L205.

Q-DLT-182: What are the key features of Delta Lake? (The Top 8)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L252.

Q-DLT-183: How does Delta Lake ensure ACID transactions?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L275.

Q-DLT-184: What is the Delta Transaction Log (_delta_log)? What's inside each commit?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L309.

Q-DLT-185: What are checkpoint files? Why are they critical?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L364.

Q-DLT-186: How does Optimistic Concurrency Control work?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L400.

Q-DLT-187: Write a MERGE from memory (upsert with all clauses)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L445.

Q-DLT-188: How do you handle duplicate keys in source during MERGE?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L498.

Q-DLT-189: How to make MERGE faster? (6 techniques)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L529.

Q-DLT-190: MERGE with Schema Evolution

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L585.

Q-DLT-191: What is OPTIMIZE? When is it good? When is it bad?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L608.

Q-DLT-192: What is VACUUM? What are the risks?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L664.

Q-DLT-193: The Small File Problem β€” Root Causes and Solutions

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L721.

Q-DLT-194: Partitioning β€” When is it good? When is it bad?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L782.

Q-DLT-195: Z-ORDER β€” How does it work internally?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L821.

Q-DLT-196: Liquid Clustering β€” The Modern Replacement

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L859.

Q-DLT-197: Data Skipping

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L903.

Q-DLT-198: Time Travel β€” Query any version of your table

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L946.

Q-DLT-199: Scenario: Accidental DELETE β€” How to recover?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L981.

Q-DLT-200: Deep Clone vs Shallow Clone

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1010.

Q-DLT-201: Change Data Feed (CDF)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1038.

Q-DLT-202: Deletion Vectors β€” Faster DELETEs and UPDATEs

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1101.

Q-DLT-203: Delta Sharing β€” Share data without copying

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1150.

Q-DLT-204: UniForm β€” One table, all formats

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1181.

Q-DLT-205: Schema Evolution β€” All the options

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1209.

Q-DLT-206: Important Delta Table Properties (Know These!)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1252.

Q-DLT-207: Delta Lake vs Apache Iceberg vs Apache Hudi

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1291.

Q-DLT-208: Managed vs External Tables

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1321.

Q-DLT-209: Predictive Optimization β€” Auto-maintenance

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1348.

Q-DLT-210: Scenario: Design a Delta Lake pipeline for a real-time order system

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1366.

Q-DLT-211: Common production gotchas with Delta Lake

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1406.

Q-DLT-212: What's new in Delta Lake 4.x?

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1427.

Q-DLT-213: Gotcha 1: VACUUM RETAIN 0 HOURS β€” "Invalid retention" Error

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1881.

Q-DLT-214: Gotcha 2: Schema Mismatch β€” "A schema mismatch detected when writing"

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1906.

Q-DLT-215: Gotcha 3: ConcurrentAppendException

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1933.

Q-DLT-216: Gotcha 4: MERGE with Duplicate Keys in Source

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1965.

Q-DLT-217: Gotcha 5: Small Files Problem in Streaming

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L1991.

Q-DLT-218: Gotcha 6: "Delta Table not found" when path looks correct

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L2015.

Q-DLT-219: MOCK INTERVIEW β€” Live Walkthrough

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L2040.

Q-DLT-220: Scenario: Senior Data Engineer Round (30 min Delta deep-dive)

Answer owner: Open the canonical concept or runnable pattern.

Alternate source wording: content/delta/Delta_01_Complete_Guide.md#L2044.

90 seconds

Practice sprint

Close the atlas. Rebuild the map.

Name the path from API to files, then explain where shuffle, skew, and serialization enter the system.

Open interview prompts