intermediate

Joins

10 min readLast updated: 2026-07-08

Overview

Learn how to merge data from two Spark DataFrames based on a common key, and understand the supported join types.

What You Will Learn

In this lesson, you will learn:
  • Join API: Joining two DataFrames on a key column.
  • Join Types: Inner, Left Outer, Right Outer, and Full Outer joins.
  • Performance Optimization: Managing network shuffles during join operations.

Detailed Concept Explanation

Joins combine columns from two DataFrames based on a shared key.

The syntax is: df1.join(df2, df1.key == df2.key, "join_type")

Supported join types:

  • inner (default): Returns rows with matching keys in both DataFrames.
  • left or left_outer: Returns all rows from the left DataFrame, plus matching rows from the right.
  • right or right_outer: Returns all rows from the right DataFrame, plus matching rows from the left.
  • outer or full: Returns all rows from both DataFrames, filling missing values with nulls.

Code Examples

Input Dataset Preview

Below are the users and orders datasets:

Users DataFrame:

userIdname
1Alice
2Bob

Orders DataFrame:

orderIdcustomerIdamount
5001199.9
5002349.9

Python (PySpark) Implementation

python
from pyspark.sql import SparkSession

spark = SparkSession.builder.appName("Joins").getOrCreate()
users = spark.createDataFrame([(1, "Alice"), (2, "Bob")], ["userId", "name"])
orders = spark.createDataFrame([(5001, 1, 99.9), (5002, 3, 49.9)], ["orderId", "customerId", "amount"])

# Perform an inner join on user ID
joined_df = users.join(orders, users.userId == orders.customerId, "inner")
joined_df.show()

Expected Output

text
+------+-----+-------+----------+------+
|userId| name|orderId|customerId|amount|
+------+-----+-------+----------+------+
|     1|Alice|   5001|         1|  99.9|
+------+-----+-------+----------+------+

Execution Plan Diagram (Python & Scala)

Execution Plan Diagram
SparkSession.builder
createDataFrame(users)
createDataFrame(orders)
join(users.userId == orders.customerId
inner)
show()

Scala Implementation

scala
import org.apache.spark.sql.SparkSession

val spark = SparkSession.builder().appName("JoinsScala").getOrCreate()
import spark.implicits._

val users = Seq((1, "Alice"), (2, "Bob")).toDF("userId", "name")
val orders = Seq((5001, 1, 99.9), (5002, 3, 49.9)).toDF("orderId", "customerId", "amount")

val joined = users.join(orders, users("userId") === orders("customerId"), "inner")
joined.show()

Expected Output

text
+------+-----+-------+----------+------+
|userId| name|orderId|customerId|amount|
+------+-----+-------+----------+------+
|     1|Alice|   5001|         1|  99.9|
+------+-----+-------+----------+------+

SQL Implementation

sql
SELECT u.userId, u.name, o.orderId, o.amount 
FROM users u 
INNER JOIN orders o ON u.userId = o.customerId;

Expected Output

Executing this SQL query returns:

userIdnameorderIdamount
1Alice500199.9

Common Mistakes

  • Key Name Ambiguity: If both DataFrames contain a column with the exact same name (e.g. id), referring to col("id") after the join will throw a "Reference is ambiguous" exception. To avoid this, rename the key columns before joining, or use the syntax: df1.join(df2, "id") which drops the duplicate key column automatically.

Best Practices

  • Broadcast Small DataFrames: If one of the DataFrames is small (e.g. under 10MB), use a broadcast join (broadcast(small_df)) to copy the small dataset to all nodes, eliminating the need to shuffle the large dataset.

Interview Perspective

What is a broadcast join in Spark and when should you use it?

A broadcast join copies a small DataFrame to all executors, allowing Spark to perform the join locally on each node without shuffling the large DataFrame. You should use it when one of your datasets is small enough to fit in executor memory (typically under 10MB by default, configured via spark.sql.autoBroadcastJoinThreshold).


Interactive Challenges

Challenge 1: Left Join Syntax (Beginner)

Write a PySpark join statement to left-join DataFrame df1 and df2 on the column 'id'.

Related Topics