Skip to main content

ETL Pipelines in PySpark โ€” End-to-End Example

At DataVerse Labs, the Data Engineering team processes millions of daily transactions from various systems โ€” API feeds, CSV exports, app logs, and real-time streams.
To unify all this, they built a PySpark ETL pipeline that:

  1. Extracts data from multiple sources
  2. Transforms it using business rules
  3. Loads it into a warehouse (Snowflake/Delta/Hive)

This chapter shows you a clean, professional, end-to-end ETL pipeline you can use in any enterprise environment.


1. ETL Architecture Overviewโ€‹

RAW DATA (CSV, JSON, DB) โ†“


Spark Extraction
โ†“
Data Cleansing & Validation
โ†“
Business Transformations
โ†“
Aggregations
โ†“
Data Warehouse
(Snowflake/Delta/Hive)

The pipeline we build will simulate a Retail Orders ETL Pipeline.


2. Step 1 โ€” Extract Phaseโ€‹

We load CSV, JSON, and Parquet files into Spark.

Example Input Files:โ€‹

orders.csvโ€‹

order_id,customer_id,amount,timestamp
O1,C101,200,2024-01-01 10:00:00
O2,C102,450,2024-01-01 10:15:00
O3,C103,,2024-01-01 10:40:00

customers.jsonโ€‹

{"customer_id": "C101", "country": "USA"}
{"customer_id": "C102", "country": "India"}
{"customer_id": "C103", "country": "UK"}

Extraction Codeโ€‹

df_orders = spark.read \
.option("header", "true") \
.option("inferSchema", "true") \
.csv("/data/orders.csv")

df_customers = spark.read.json("/data/customers.json")

df_orders.show()
df_customers.show()

Output (orders.csv)

+--------+-----------+------+-------------------+
|order_id|customer_id|amount|timestamp |
+--------+-----------+------+-------------------+
|O1 |C101 |200 |2024-01-01 10:00:00|
|O2 |C102 |450 |2024-01-01 10:15:00|
|O3 |C103 |null |2024-01-01 10:40:00|
+--------+-----------+------+-------------------+

3. Step 2 โ€” Cleansing & Validationโ€‹

3.1 Fix Null Valuesโ€‹

from pyspark.sql.functions import col

df_orders_clean = df_orders.fillna({"amount": 0})

3.2 Validate Customer IDsโ€‹

df_orders_valid = df_orders_clean.filter(col("customer_id").isNotNull())

4. Step 3 โ€” Business Transformationsโ€‹

Join Orders with Customer Infoโ€‹

df_joined = df_orders_valid.join(
df_customers,
on="customer_id",
how="left"
)

Add Sales Categoryโ€‹

from pyspark.sql.functions import when

df_transformed = df_joined.withColumn(
"sales_category",
when(col("amount") > 300, "HIGH")
.when(col("amount") > 100, "MEDIUM")
.otherwise("LOW")
)

Output Exampleโ€‹

+-----------+--------+------+-------------------+-------+--------------+
|customer_id|order_id|amount|timestamp |country|sales_category|
+-----------+--------+------+-------------------+-------+--------------+
|C101 |O1 |200 |2024-01-01 10:00:00|USA |MEDIUM |
|C102 |O2 |450 |2024-01-01 10:15:00|India |HIGH |
|C103 |O3 |0 |2024-01-01 10:40:00|UK |LOW |
+-----------+--------+------+-------------------+-------+--------------+

5. Step 4 โ€” Aggregation Layerโ€‹

Daily Sales Summaryโ€‹

from pyspark.sql.functions import to_date, sum as spark_sum

df_daily_summary = df_transformed \
.withColumn("date", to_date("timestamp")) \
.groupBy("date", "country") \
.agg(spark_sum("amount").alias("total_sales"))

Output

+----------+-------+-----------+
|date |country|total_sales|
+----------+-------+-----------+
|2024-01-01|USA |200 |
|2024-01-01|India |450 |
|2024-01-01|UK |0 |
+----------+-------+-----------+

6. Step 5 โ€” Load Phaseโ€‹

You may load into:

  • Snowflake
  • Delta Lake
  • Hive

Example: Write to Delta Lakeโ€‹

df_transformed.write \
.format("delta") \
.mode("overwrite") \
.save("/warehouse/processed/orders")

Example: Write to Hiveโ€‹

df_daily_summary.write \
.mode("overwrite") \
.saveAsTable("dv_dw.daily_sales")

Example: Write to Snowflakeโ€‹

df_daily_summary.write \
.format("snowflake") \
.options(**sf_options) \
.option("dbtable", "DAILY_SALES") \
.mode("overwrite") \
.save()

7. Production-Grade ETL: Best Practicesโ€‹

โœ” Use explicit schemas for large files
โœ” Track ETL runs with audit columns (created_at, batch_id)
โœ” Store raw โ†’ cleaned โ†’ curated layers separately
โœ” Use Delta Lake for versioned tables
โœ” Handle bad records using badRecordsPath
โœ” Maintain checkpoints for streaming ETL
โœ” Schedule ETL with Airflow, Oozie, or Databricks Jobs


Summaryโ€‹

In this chapter, you built an end-to-end ETL pipeline in PySpark:

โœ” Extractionโ€‹

  • Load CSV, JSON, Parquet

โœ” Transformationโ€‹

  • Cleansing
  • Validation
  • Business rules
  • Joins & nested logic

โœ” Loadingโ€‹

  • Delta Lake
  • Hive
  • Snowflake

This pipeline mirrors what real enterprise data engineering teams deploy at scale.


Next Topic โ†’ Real Company Use Cases โ€” Support, Development, & Production Workflows