Data Transformation and Modeling · Topic 2 of 3

Joins, Deduplication, Aggregation and Spark Tuning

Databricks DEA joins and unions, broadcast joins, dropDuplicates, count, approx_count_distinct, mean and summary, plus the basic Spark tuning settings.

  • 5 min
In a nutshell
  • Inner is the default join; left keeps every left row; cross has no key.
  • Join on a list of names for multiple keys and one copy of each key column.
  • SQL UNION removes duplicates; UNION ALL and PySpark union keep them.

What it is

Silver tables are combined and summarized to answer business questions. Joins match rows across DataFrames by key, unions stack DataFrames with the same columns, deduplication removes repeated records and aggregations compute counts and averages.

Joins and aggregations shuffle data across the cluster, so a few Spark settings control how fast they run. The exam expects you to know what each setting changes and to re-measure after changing it.

Silver DataFramesJoin or unionDeduplicateAggregateGold result

How it works

Join types

JoinKeeps
Inner (default)Only rows with a match on both sides
Left (left outer)All left rows; right columns are null where no match
CrossEvery left row paired with every right row; no key
BroadcastSame result as the join type used; the small side is copied to every executor
from pyspark.sql import functions as F
result = (orders
  .join(customers, ["customer_id", "region"], "left")
  .join(F.broadcast(countries), "country_code"))
pairs = sizes.crossJoin(colors)

Passing a list of column names joins on multiple keys and keeps one copy of each key column. A condition such as orders.id == customers.id keeps both columns, which then need dropping or renaming. In SQL, /*+ BROADCAST(c) */ is the broadcast hint.

A broadcast join avoids shuffling the large table. It fits when one side is small enough to fit in executor memory, such as a lookup table.

Union and union all

OperationDuplicatesMatches columns by
SQL UNIONRemovedPosition
SQL UNION ALLKeptPosition
PySpark union / unionAllKeptPosition
PySpark unionByNameKeptName

PySpark union behaves like SQL UNION ALL. Add .distinct() to remove duplicates, and use unionByName when the column order differs between the DataFrames.

Deduplication

distinct() removes rows that are identical in every column. dropDuplicates(["order_id"]) keeps one row per key and ignores the other columns; which row survives is not guaranteed.

To keep a specific row, such as the latest version, rank rows in a window and keep the first:

from pyspark.sql import Window, functions as F
w = Window.partitionBy("order_id").orderBy(F.col("updated_at").desc())
latest = (orders.withColumn("rn", F.row_number().over(w))
  .filter("rn = 1").drop("rn"))

Aggregations

from pyspark.sql import functions as F
stats = orders.groupBy("region").agg(
  F.count("*").alias("orders"),
  F.approx_count_distinct("customer_id").alias("customers"),
  F.mean("amount").alias("avg_amount"))
FunctionWhat it returns
count("*")All rows, including nulls
count("col")Rows where col is not null
countDistinct("col")Exact distinct count; costly on large data
approx_count_distinct("col")Estimated distinct count, faster; default maximum relative standard deviation 0.05
mean / avgAverage of non null values
summary()count, mean, stddev, min, quartiles and max per column

describe() gives count, mean, stddev, min and max; summary() adds the 25%, 50% and 75% percentiles. In SQL the function is also approx_count_distinct.

Basic tuning parameters

SettingWhat it controlsWhen to change it
spark.sql.shuffle.partitionsPartitions after a DataFrame or SQL shuffle (joins, groupBy); open source default 200Fewer for small data, more for very large shuffles
spark.default.parallelismDefault partitions for RDD operationsRarely for DataFrame code; it does not set DataFrame shuffle partitions
spark.executor.memory / spark.driver.memoryMemory per executor and for the driverExecutor out of memory or spilling; driver failing on collect
spark.sql.autoBroadcastJoinThresholdMaximum table size broadcast automatically; default 10 MB, -1 disablesRaise for a slightly larger lookup table

Session settings such as shuffle partitions change at runtime with spark.conf.set. Memory settings are fixed when the cluster starts, so they go in the cluster's Spark config. Serverless compute allows only a few properties, including spark.sql.shuffle.partitions, and manages memory itself.

spark.conf.set("spark.sql.shuffle.partitions", "64")
spark.conf.set("spark.sql.autoBroadcastJoinThreshold", 50 * 1024 * 1024)

Adaptive query execution, on by default, can coalesce shuffle partitions and switch to a broadcast join at runtime. Change one setting at a time and re-measure with the same data, comparing duration and stage metrics in the Spark UI.

Baseline runChange one settingRerun same dataCompare duration and stage metricsKeep or revert

How the exam asks about this

  • "Join a 2 TB fact table to a 5 MB country lookup without shuffling the fact table." Use a broadcast join.
  • "Stack two monthly DataFrames and keep every row, columns in a different order." Use unionByName.
  • "Count unique visitors on billions of rows, where an estimate is acceptable and speed matters." Use approx_count_distinct.

What to remember for the exam

  • Inner is the default join; left keeps every left row; cross has no key.
  • Join on a list of names for multiple keys and one copy of each key column.
  • SQL UNION removes duplicates; UNION ALL and PySpark union keep them.
  • dropDuplicates(subset) keeps one arbitrary row per key; use row_number over a window to choose the row.
  • count("col") skips nulls; approx_count_distinct trades exactness for speed.
  • spark.sql.shuffle.partitions sets DataFrame shuffle partitions; memory settings need a cluster restart.
Watch out

spark.default.parallelism applies to RDDs. Changing it does not change the number of partitions after a DataFrame join or groupBy; that is spark.sql.shuffle.partitions.

Common mistakes

Expecting PySpark union to remove duplicates. It keeps them like UNION ALL and matches by position, so swapped columns silently mix values.

Broadcasting a large table. A broadcast copies the table to every executor; a large one causes out of memory errors instead of speed.

Setting executor memory from a notebook. spark.executor.memory is read when executors start. Set it in the cluster configuration and restart.

Check yourself

  • Which PySpark method stacks two DataFrames whose columns are in a different order?

    unionByName. union and unionAll match columns by position and keep duplicates.

  • How do you keep only the most recent row for each order_id?

    Rank rows with row_number() over a window partitioned by order_id and ordered by update time descending, then keep row 1. dropDuplicates does not guarantee which row survives.

  • Which setting changes the number of partitions after a DataFrame groupBy?

    spark.sql.shuffle.partitions. It applies to joins and aggregations in DataFrame and SQL code.

Topic 2 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.