Data Transformation and Modeling · Topic 1 of 3

Cleaning and Reshaping Data from Bronze to Silver

Databricks DEA bronze to silver cleaning: handle nulls, cast types, add, drop, rename and split columns, filter rows and explode arrays in PySpark and SQL.

  • 5 min
In a nutshell
  • Bronze is raw, silver is cleaned and typed; write silver as a new Unity Catalog table.
  • dropna(subset=...) drops rows, fillna and coalesce replace nulls.
  • cast fails on bad values under ANSI mode; try_cast returns NULL.

What it is

In the medallion architecture, a bronze table holds data as it was ingested, and a silver table holds the same data cleaned: required values present, consistent types and tidy column names. Cleaning is a read, transform and write: read the bronze table, fix it, and write a new silver table in Unity Catalog.

The exam shows this work in PySpark DataFrame code or in SQL. You need to recognize which function does each job and what it does to rows that do not fit.

Bronze tablereadDataFrame or SELECTFix nulls and typesReshape columns and rowswriteSilver table

How it works

Handling nulls

NeedPySparkSQL
Remove rows missing a keydropna(subset=["id"]) or na.dropWHERE id IS NOT NULL
Replace nulls with a defaultfillna({"country": "unknown"})coalesce(country, 'unknown')
Pick the first non null valueF.coalesce("a", "b")coalesce(a, b)

dropna() with no arguments drops a row if any column is null, which is rarely what you want. Name the key columns with subset.

Standardizing data types

Bronze data often arrives as strings. Cast it to the real type with cast and parse dates with to_date or to_timestamp and a format string.

With ANSI mode on, cast raises an error on a value it cannot convert. try_cast returns NULL for that value instead, so the load continues and you can filter or quarantine the bad rows.

from pyspark.sql import functions as F
silver = (spark.read.table("main.bronze.orders")
  .dropna(subset=["order_id"])
  .fillna({"country": "unknown"})
  .withColumn("amount", F.col("amount").cast("decimal(10,2)"))
  .withColumn("order_date", F.to_date("order_ts")))
silver.write.mode("overwrite").saveAsTable("main.silver.orders")

The same step in SQL is a CREATE TABLE AS SELECT:

CREATE OR REPLACE TABLE main.silver.orders AS
SELECT order_id, try_cast(amount AS DECIMAL(10,2)) AS amount,
       coalesce(country, 'unknown') AS country, to_date(order_ts) AS order_date
FROM main.bronze.orders
WHERE order_id IS NOT NULL;

Adding, dropping and renaming columns

TaskPySparkSQL
Add or replace a columnwithColumn("tax", F.col("amount") * 0.2)expression with AS tax
Drop columnsdrop("raw_json", "ingest_file")leave them out of the SELECT
Rename a columnwithColumnRenamed("cust_id", "customer_id")cust_id AS customer_id
Keep only some columnsselect("id", "amount")SELECT id, amount

withColumn with an existing name replaces that column. DataFrames are immutable, so each call returns a new DataFrame; assign the result or chain the calls.

Changing the table structure

To change a stored Delta table in place, use ALTER TABLE: ADD COLUMNS, RENAME COLUMN and DROP COLUMN. Rename and drop work only when column mapping is enabled on the table.

ALTER TABLE main.silver.orders SET TBLPROPERTIES ('delta.columnMapping.mode' = 'name');
ALTER TABLE main.silver.orders RENAME COLUMN cust_id TO customer_id;
ALTER TABLE main.silver.orders ADD COLUMNS (channel STRING);

Splitting columns and exploding arrays

split(col, pattern) turns a string into an array; take parts with getItem(0) or [0]. The pattern is a regular expression, so a literal dot or pipe must be escaped.

explode turns each array element into its own row and copies the other columns. Rows whose array is null or empty disappear. explode_outer keeps those rows with a null value.

from pyspark.sql import functions as F
df = (orders
  .withColumn("first_name", F.split("full_name", " ").getItem(0))
  .withColumn("item", F.explode("items"))
  .filter(F.col("item.qty") > 0))

Nested struct fields are read with dot notation, such as item.qty or address.city in SQL.

Filtering rows

filter and where are the same method. Combine conditions with &, | and ~ in PySpark, each condition in parentheses; SQL uses AND, OR and NOT.

Order rowitems array of 3explode3 item rowsorder columns copiedfilterRows with qty above 0

How the exam asks about this

  • "Orders with an empty items array must still appear in the silver table." Use explode_outer, not explode.
  • "A bad amount string must not stop the load; it should become NULL." Use try_cast.
  • "Remove only rows where customer_id is null." Use dropna(subset=["customer_id"]) or WHERE customer_id IS NOT NULL.

What to remember for the exam

  • Bronze is raw, silver is cleaned and typed; write silver as a new Unity Catalog table.
  • dropna(subset=...) drops rows, fillna and coalesce replace nulls.
  • cast fails on bad values under ANSI mode; try_cast returns NULL.
  • withColumn adds or replaces, withColumnRenamed renames, drop removes.
  • explode drops null and empty arrays; explode_outer keeps them.
  • split takes a regular expression and returns an array.
Watch out

withColumn and withColumnRenamed return a new DataFrame. Code that calls them without assigning the result leaves the original DataFrame unchanged.

Common mistakes

Calling dropna with no subset. Without subset, one null in any optional column removes the whole row, and the silver table loses valid data.

Using explode when rows must survive. A record whose array is empty vanishes with explode. If every parent row must stay, the answer is explode_outer.

Renaming a Delta column without column mapping. ALTER TABLE ... RENAME COLUMN fails until delta.columnMapping.mode is set to name; in a DataFrame, rename with withColumnRenamed before writing.

Check yourself

  • Which function keeps rows whose array column is null when you flatten it?

    explode_outer. It returns one row with a null element, while explode drops those rows.

  • How do you convert a string to a decimal so that bad values become NULL instead of failing the query?

    Use try_cast(amount AS DECIMAL(10,2)). Under ANSI mode a plain cast raises an error on a value it cannot convert.

  • What does dropna(subset=["order_id"]) remove?

    Only rows where order_id is null. Nulls in other columns do not cause a row to be dropped.

Topic 1 of 3 in Data Transformation and Modeling

Test yourself on Data Transformation and Modeling

The full Databricks Data Engineer Associate question bank has 598 questions, with timed practice exams and an explanation for every answer.

This study guide is independent and is not affiliated with, endorsed by, or sponsored by Databricks. Vendor names, certification names and exam codes are trademarks of their respective owners and are used for identification only.