How to create a Delta Table Lakehouse with ACID in Microsoft Fabric
This tutorial explains how to create a Delta Table in a Microsoft Fabric Lakehouse and how to guarantee essential ACID properties: atomicity (transaction), read consistency and schema management. Knowing how to create and manage Delta Tables is useful for ETL/ELT pipelines, data history, auditing and integration with Spark and Power BI. At the end you will have small practical tests to verify that the basic operations work and to inspect the metadata.
Prerequisites
- An account with access to a Microsoft Fabric workspace with edit permissions (at least Contributor on the workspace).
- A Lakehouse created in the workspace (OneLake integrated) and the Lakehouse path available. E.g.: lakehouse://my_workspace/my_lakehouse/tables.
- Basic knowledge of Spark SQL or PySpark and access to a Notebook or Spark Job inside the workspace.
- Recommended: test first in a development environment with small datasets (100–10,000 rows) before migrating to production.
Step 1: Prepare the environment and the connection to the Lakehouse
Open a Notebook in the Microsoft Fabric workspace (Python or Spark SQL). Verify the Lakehouse path you will use. We will use Spark SQL for compatibility with other Fabric areas and to reuse in Jobs. The goal is to set a path variable and ensure permissions allow writing to OneLake.
-- exemplo Spark SQL: definir caminho do Lakehouse
SET lakehouse_path = 'lakehouse://<seu_workspace>/<seu_lakehouse>/tables';
Specifically, confirm the exact name (case-sensitive) in the Lakehouse UI. If you prefer Python, you can read the setting with spark.conf.get('lakehouse_path') after defining it in the Notebook.
Step 2: Create a directory for the Delta Table and write initial data
A Delta Table in a Lakehouse is materialized in OneLake as Parquet files and a _delta_log directory with metadata. First create a small DataFrame (e.g., 3–5 rows) and write it as Delta. This creates the layout and the first commit which guarantees atomicity of the initial operation.
-- Spark SQL: criar tabela temporária e gravar em formato delta
CREATE OR REPLACE TEMP VIEW vendas_tmp AS
SELECT * FROM VALUES
(1, '2026-01-01', 100.0),
(2, '2026-01-02', 150.5)
AS t(id, data_venda, valor);
-- gravar como Delta table no Lakehouse
CREATE TABLE IF NOT EXISTS delta.`${lakehouse_path}/vendas_delta`
USING DELTA
AS SELECT * FROM vendas_tmp;
After running, verify: the CREATE TABLE operation generates a commit in the _delta_log (a JSON metadata file). If this operation fails, the table will not have been created and there will be no valid Parquet files.
Step 3: Read the Delta Table with consistency and test transactions
One advantage of Delta is that reads obtain a consistent snapshot of the table state even if concurrent writes occur. Test a write operation followed by a read to confirm the basic ACID behavior.
-- Exemplo: adicionar linha (transacção simples)
INSERT INTO delta.`${lakehouse_path}/vendas_delta` VALUES (3, '2026-01-03', 200.0);
-- Ler a tabela após a escrita
SELECT * FROM delta.`${lakehouse_path}/vendas_delta` ORDER BY id;
In a concurrency scenario, a reader that started before a commit will see the previous version; a reader that starts after the commit will see the new version. This ensures read isolation. In practical tests, inserting 100–1,000 rows and measuring commit time (usually seconds, depending on file size) helps calibrate workloads.
Step 4: Safely update schema (schema evolution)
If you need to add a column, use schema evolution so as not to break old readers. Delta supports controlled schema changes. You can choose to alter the table explicitly or write with mergeSchema.
-- Adicionar coluna opcional 'cliente' sem falhar com schema evolution
ALTER TABLE delta.`${lakehouse_path}/vendas_delta`
ADD COLUMNS (cliente STRING);
-- Ou gravar com opção de mergeSchema em operações de escrita via DataFrame (PySpark)
# Python (PySpark) exemplo mínimo
from pyspark.sql import SparkSession
spark = SparkSession.builder.getOrCreate()
df = spark.createDataFrame([(4, '2026-01-04', 75.0, 'Cliente A')], ['id','data_venda','valor','cliente'])
df.write.format('delta').mode('append').option('mergeSchema','true').save(spark.conf.get('lakehouse_path') + '/vendas_delta')
In production environments, adopt policies: for example, allow only optional (nullable) columns or follow a review process for schema changes. Test with 1–2 new columns and confirm that existing applications continue to function.
Step 5: Use basic Time Travel to recover versions
Delta allows querying older versions (Time Travel). This is useful to undo changes or audit the history. Each commit increments the version — start by checking version 0, 1, etc. In small tables this is immediate; in large tables querying older versions remains indexed through the _delta_log.
-- Ver a versão anterior (exemplo: versão 0, 1, ...)
SELECT * FROM delta.`${lakehouse_path}/vendas_delta` VERSION AS OF 0;
-- Ou usar timestamp (exemplo)
SELECT * FROM delta.`${lakehouse_path}/vendas_delta` TIMESTAMP AS OF '2026-01-01 00:00:00';
Try: make 3 commits (inserts) and query VERSION AS OF 1 to see the intermediate state. This facilitates auditing and point-in-time recoveries.
Verify the result
Confirm that the vendas_delta folder exists in the Lakehouse (OneLake) and that the _delta_log and parquet files are present. In the Notebook, you can list the directory with interface commands (e.g.: %fs ls) or use APIs:
-- exemplo (Notebook): listar ficheiros
%fs ls /lakehouse///tables/vendas_delta
-- ou em PySpark
spark.read.format('delta').load(spark.conf.get('lakehouse_path') + '/vendas_delta').show()
Look for files like _delta_log/00000000000000000001.json (example) and parquet files named like part-00000-... .parquet. Run a final SELECT and verify the rows, the new column 'cliente' and that previous versions return the expected data. If everything is as expected, you have a Delta Table ready for use with Power BI (Direct Lake) or to integrate into ETL/ELT pipelines.
Conclusion
You created a Delta Table in the Microsoft Fabric Lakehouse, tested writing, consistent reading, schema change and basic Time Travel. Recommended next steps: automate writes with a scheduled Spark Job, configure log retention and optimizations such as compaction/OPTIMIZE for tables with millions of rows, and expose the table to Power BI with Direct Lake. Tip: before production operations, test schema evolution and recoveries with Time Travel in a development environment to avoid conflicts and data loss.