Create Your First Delta Table
Create your first Delta table in Databricks — hands-on steps, troubleshooting, and what to study next.
Focus: create your first delta table
You've built DataFrames, run transformations, and queried data in notebooks — but every time you restart a cluster, that work vanishes. Your data pipeline currently ends in a dead end: results exist only in memory, not as a durable, queryable asset your team can trust. The solution is Delta Lake, and the first step is learning how to create your first Delta table — the foundation of every reliable data pipeline on Databricks. In this lesson, you'll move from ephemeral DataFrames to persistent, versioned tables that support ACID transactions, time travel, and schema evolution.
The problem this lesson solves
Imagine you've spent hours cleaning and transforming a dataset, but when you try to share your results with a colleague, they can't see them. Or worse, the next day your pipeline fails because an upstream file changed format without notice. These are classic symptoms of working with raw files or in-memory data without a governed storage layer.
Raw Parquet or CSV files on cloud storage have no transaction support, no schema enforcement, and no versioning. If two jobs write to the same directory concurrently, you can corrupt your data. Delta Lake solves these problems by adding a transaction log and ACID guarantees on top of your existing storage. Creating your first Delta table is the single action that turns a collection of files into a reliable, queryable dataset.
Think of a Delta table as a database table that lives on your cloud storage — it's not a separate service, just a directory with a special
_delta_logfolder that tracks every change.
Core concept / mental model
The fastest way to grasp Delta tables is by analogy: a Delta table is like a bank ledger. Every transaction (insert, update, delete) is recorded in a log before the data changes. This log ensures that all reads see a consistent snapshot, even if multiple writers are active. If something goes wrong, you can roll back to a previous state — exactly like reviewing a bank statement.
Here are the key components you'll work with:
- Table location: A directory on cloud storage (e.g.,
dbfs:/mnt/delta/events). - Data files: Parquet files that store the actual rows.
- Transaction log: A
_delta_logsubdirectory containing JSON files — each file is a commit that describes what changed. - Table metadata: Schema, partitioning, and configuration stored in the log.
Delta tables support standard SQL and DataFrame operations, so you don't need to learn a new language — you just wrap your existing Spark code with the DELTA format.
How it works step by step
Creating a Delta table from a DataFrame is a three-step process:
- Load your source data into a Spark DataFrame using any of Databricks' connectors (Parquet, CSV, JSON, JDBC, or cloud storage).
- Write the DataFrame using
.write.format("delta")and specify a path. This creates the table directory and initializes the transaction log. - Register the table in the metastore (optional) with
CREATE TABLE ... USING DELTAordf.write.saveAsTable(), so it appears in the Data Explorer and can be queried by name.
The critical point: the first write creates the schema and the table. Every subsequent write appends or overwrites data, and each write appends a new commit to the transaction log.
Hands-on walkthrough
Let's create your first Delta table in a Databricks notebook. We'll start with a small CSV dataset and work through the standard patterns.
1. Create a Delta table from a CSV
First, load a sample CSV from the Databricks datasets:
# Load sample data
df = spark.read.csv("/databricks-datasets/COVID/covid-19-data/us-counties.csv",
header=True, inferSchema=True)
# Write to a Delta table (this is step one — your first Delta table!)
df.write.format("delta").mode("overwrite").save("/mnt/delta/covid_us") # Note: use DBFS root path in notebooks
If you run this, you'll see a message like Delta Lake updated 5 files in 0.89 seconds. Check the location with:
display(spark.sql("DESCRIBE EXTENDED delta.`/mnt/delta/covid_us`"))
You'll see the table schema, location, and format — all confirmed as Delta.
2. Register the table in the metastore
To make the table accessible by name across your workspace, register it:
# Register as a managed table in the default database
spark.sql("CREATE TABLE covid_us USING DELTA LOCATION '/mnt/delta/covid_us'")
# Or with saveAsTable (creates a managed table in the database)
df.write.saveAsTable("covid_us")
Now you can query it with SQL:
SELECT state, COUNT(*) FROM covid_us GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;
3. Append new data and verify time travel
Delta tables shine with updates. Let's append a few fake rows and then use time travel to see the original version:
# Append new data
from pyspark.sql import Row
new_rows = spark.createDataFrame([
Row(date="2021-01-01", county="Test", state="XX", fips=999, cases=0, deaths=0)
])
new_rows.write.format("delta").mode("append").save("/mnt/delta/covid_us")
# Show table history
spark.sql("HISTORY covid_us").show(5, truncate=False)
# Query version 0 (original)
spark.sql("SELECT COUNT(*) FROM covid_us VERSION AS OF 0").show()
You should see counts change, and the history shows two versions.
Compare options / when to choose what
When creating a Delta table, you have several choices — especially around table type and write modes. Here's a quick guide:
| Option | Best For | Notes |
|---|---|---|
Managed table (saveAsTable) |
Databricks-owned tables in a catalog | Files are deleted when the table is dropped; easier to manage |
External table (LOCATION with USING DELTA) |
Tables with existing data on cloud storage | Drops only the metadata, not the files |
.mode("overwrite") |
Full refresh — replace entire table | Works atomically in Delta; replaces the whole directory |
.mode("append") |
Incremental loads — new events daily | Adds new files without disturbing existing data |
MERGE (upsert) |
Slowly changing dimensions or deduplication | Most flexible; updates/inserts/deletes in one operation |
For most ETL pipelines, start with external tables if data already lives in cloud storage, otherwise use managed tables. Use append for log-style data and merge for transactional changes.
Troubleshooting & edge cases
"Path does not exist" when reading a Delta table
Ensure the path is correct and permissions allow access. In Databricks, use dbfs:/ for DBFS paths, or the direct cloud path (e.g., s3://). Check with dbutils.fs.ls().
Schema mismatch when appending
If the new DataFrame has a different column order or data type, the write may fail. Use .option("mergeSchema", "true") to allow schema evolution:
df_new.write.format("delta").option("mergeSchema", "true").mode("append").save("/mnt/delta/covid_us")
"Cannot resolve 'column'" in SQL after saving as table
Your query uses a column that doesn't exist — check the schema with DESCRIBE covid_us.
Table appears empty after overwrite
Did you use mode("overwrite")? It replaces the entire table — if you intended to update a few rows, use MERGE.
Overwriting a table while other jobs are reading
Delta's optimistic concurrency control will prevent lost updates — you may get a conflict error. Retry the operation or use MERGE to avoid conflicts.
What you learned & what's next
You've created your first Delta table, registered it, appended data, and used time travel — that's the ACID foundation. You can explain how a Delta table stores data in Parquet plus a transaction log, and you can confidently choose between managed/external tables and write modes.
Next up: In the next lesson, you'll learn to read and transform data from your Delta table — including how to handle schema evolution and optimize performance with Z-order and partitioning. Now that you have a durable asset, you can build streaming and batch pipelines that everyone can trust.
Practice saving the same DataFrame to managed and external tables, then drop one and observe the behavior — it's the fastest way to internalize the difference.
Practice recap
Try creating a Delta table from a new dataset (e.g., /databricks-datasets/nyctaxi). Append a few rows, run HISTORY, then query a previous version. Also create an external table using CREATE TABLE ... USING DELTA LOCATION and confirm the files remain after dropping the table. This will solidify the difference between managed and external tables.
Common mistakes
- Using
mode("overwrite")for every write — erases existing data when you only meant to append. - Forgetting to register the table in the metastore — then you can't query it by name in SQL.
- Writing to a path that is already a Parquet table — Delta will fail or overwrite unexpectedly; use a new path or
ANALYZE TABLE. - Not handling schema evolution — appending data with different columns fails unless you set
mergeSchema. - Querying a table without refreshing the metastore after creating it from SQL — use
REFRESH TABLEif needed.
Variations
- Use
CREATE OR REPLACE TABLEfor idempotent table creation. - Use Python's
DeltaTableAPI (delta.tables.DeltaTable) for programmatic management like upserts. - Use
OPTIMIZEandZORDERafter table creation to improve query performance.
Real-world use cases
- Daily COVID-19 county data: append new data daily, time-travel to historical versions for trend analysis.
- Clickstream events: stream raw events into a Delta table with
MERGEto deduplicate and maintain a clean dataset. - Financial transactions: use ACID guarantees to safely upsert customer balances and audit changes with the transaction log.
Key takeaways
- A Delta table = Parquet files + transaction log, providing ACID, versioning, and schema evolution.
- Create a table with
.write.format("delta")or SQLCREATE TABLE USING DELTA; register it in the metastore for SQL access. - Append vs. overwrite: use
mode("append")for incremental loads,overwritefor full refresh — never casually. - Time travel lets you query previous versions using
VERSION AS OF— invaluable for debugging and reproducibility. - Managed tables are easy for Databricks-owned data; external tables preserve files when you drop metadata.
- Troubleshoot schema mismatches with
mergeSchemaand check file paths withdbutils.fs.ls().
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.