How to partition a Delta table in the Fabric Lakehouse
Partitioning a Delta table in the Lakehouse is a simple technique that can make queries much faster when you work with large volumes of data. The idea is to store rows in separate folders by a column — for example, the year — so the engine reads only the part it needs instead of scanning the whole table. It is a good way to improve performance without changing how you write SQL. It works especially well on large fact tables, with millions of rows and several years of history.
Prerequisites
- A Microsoft Fabric workspace with assigned capacity.
- A Lakehouse already created in your workspace.
- A Fabric notebook attached to that Lakehouse, with Spark available.
- A table or file with a column suitable for partitioning (date, year, region…).
- Basic knowledge of PySpark and SQL.
Step 1: Choose a good partition column
Before writing any code, think carefully about the column. A good partition column has few distinct values (low cardinality) and appears often in your query filters. Aggregated dates — such as the year or the month — are the classic choices. Avoid partitioning by columns with thousands of unique values, like a customer ID, because that creates thousands of tiny folders and, instead of helping, makes everything slower.
Rule of thumb: aim for each partition to hold a reasonable amount of data (ideally hundreds of megabytes). If each folder has only a few small files, you are over-partitioning — pick a column with fewer values.
Step 2: Prepare the partition column
If you don't yet have a simple column to partition by, you can create one from a date. In your notebook, extract the year from the date column with the year function:
from pyspark.sql.functions import year, col
# Read the source data (a table that already exists in the Lakehouse)
df = spark.read.table("vendas")
# Create the "ano" (year) column from the sale date
df = df.withColumn("ano", year(col("data_venda")))
Now df has an ano column with values like 2024 or 2025, perfect to use as a partition.
Step 3: Write the partitioned table
Save the data as a Delta table and specify the partition column with partitionBy. Delta is the default format in the Lakehouse, so the table is immediately ready to be queried with SQL.
df.write.format("delta") \
.partitionBy("ano") \
.mode("overwrite") \
.saveAsTable("vendas_particionada")
When it saves, Fabric creates one folder per column value — for example ano=2024 and ano=2025 — and places only the matching rows in each one. We used mode("overwrite") to (re)create the table from scratch; if you only wanted to add new data, you would use mode("append").
Step 4: Query taking advantage of the partition
When you filter by the partition column, the engine reads only the folders it needs. This is called partition pruning, and it is what makes the query faster.
SELECT ano, SUM(total) AS total_ano
FROM vendas_particionada
WHERE ano = 2025
GROUP BY ano;
Because you filtered by ano = 2025, the engine ignores all the other folders and reads only that partition. The more years you have in the table, the bigger the saving.
Check the result
To confirm the table really is partitioned, run the DESCRIBE DETAIL command and look at the partitionColumns field:
DESCRIBE DETAIL vendas_particionada;
In the result, partitionColumns should show ["ano"]. You can also open the Lakehouse in the Fabric explorer and expand the table: you will see the folders ano=2024, ano=2025, and so on. If those folders appear and the filtered query returns the right numbers, the partition is working as it should.
Conclusion
In just a few steps you created a partitioned Delta table in the Lakehouse and saw how partition pruning speeds up queries without complicating your SQL. A good next step is to measure the difference: run the same query on an unpartitioned table and on a partitioned one and compare the times. Then combine the partition with the OPTIMIZE command to keep the files well organized. And a question to think about: which of your largest tables would already benefit from a good partition column?