CertSafari
    Databricks Certified Data Engineer Associate· Lessons

    Domain 3 · Lesson 13/33

    PySpark Deduplication: distinct() vs dropDuplicates()

    Perform data deduplication operations and aggregate operations on DataFrames

    9 min read
    3.14% of exam
    2 sources
    Published 2 Oct 2026
    Docs as of 30 Sep 2026

    What you will be able to do

    • Remove fully identical rows from a DataFrame with distinct()
    • Deduplicate on a chosen set of key columns with dropDuplicates(subset)
    • Explain what dropDuplicates costs on a streaming DataFrame and how withWatermark limits it

    Key concept

    Duplicate comparison scope — Two rows are duplicates only with respect to the columns you compare. distinct() always compares every column. dropDuplicates() also compares every column by default, but it can be limited to a subset of key columns, which changes how many rows survive.

    1.distinct(): removing rows that match in every column

    Bronze data often contains exact copies of the same record, for example when a source system sends the same event twice. The simplest tool for this is distinct(). It takes no arguments and returns a new DataFrame that contains only the distinct rows. Two rows count as duplicates only when every column matches. Like other DataFrame transformations, it leaves the original DataFrame unchanged and gives you a new one.

    distinct() collapses the two identical Alice rows; count() on the result returns 2python
    df = spark.createDataFrame(
        [(14, "Tom"), (23, "Alice"), (23, "Alice")], ["age", "name"])
    df.distinct().show()
    # +---+-----+
    # |age| name|
    # +---+-----+
    # | 14|  Tom|
    # | 23|Alice|
    # +---+-----+
    
    df.distinct().count()
    # 2

    Chaining count() after distinct() is the usual way to find out how many unique records a dataset really holds. Adding columns doesn't change the rule. In a second documented example, a three-column DataFrame with the rows (14, Tom, M), (23, Alice, F), (23, Alice, F) and (14, Tom, M) also reduces to two rows, because each pair matches in all three columns. distinct() has no parameter, so you can't tell it to compare only some of the columns. If two records share a business key but differ in a timestamp or some other column, distinct() keeps both.

    Checkpoint 1 of 5· Check yourself

    After running deduped = df.distinct(), what is true of df?

    Sources1

    2.dropDuplicates(subset): deduplicating on key columns

    dropDuplicates() covers both cases. Called with no arguments it compares all columns, just like distinct(). Its optional subset parameter takes a list of column names, and only those columns are used to decide whether two rows are duplicates. This is how you deduplicate on a business key while other columns differ.

    How each call decides what counts as a duplicate
    CallColumns comparedTakes a column list?
    distinct()All columnsNo
    dropDuplicates()All columns (the default)Optional, omitted here
    dropDuplicates(['name', 'height'])Only name and heightYes, through subset
    With no subset, only the exact copy of (Alice, 5, 80) is removedpython
    from pyspark.sql import Row
    df = spark.createDataFrame([
        Row(name='Alice', age=5, height=80),
        Row(name='Alice', age=5, height=80),
        Row(name='Alice', age=10, height=80)
    ])
    
    df.dropDuplicates().show()
    # +-----+---+------+
    # | name|age|height|
    # +-----+---+------+
    # |Alice|  5|    80|
    # |Alice| 10|    80|
    # +-----+---+------+
    Restricting the comparison to name and height leaves one rowpython
    df.dropDuplicates(['name', 'height']).show()
    # +-----+---+------+
    # | name|age|height|
    # +-----+---+------+
    # |Alice|  5|    80|
    # +-----+---+------+

    The row with age 10 is gone, even though its age differs. With a subset, the columns outside the subset don't stop two rows from being duplicates, but the row that survives still carries a value for each of them. In this example the kept row has age 5. The reference page doesn't say which row of a duplicate group is kept, so don't build logic on the assumption that a particular one, such as the first or the latest, will win.

    Checkpoint 2 of 5· Fill the gap

    Which method completes this call so that rows count as duplicates when only name and height match?

    df. ? (['name', 'height']).show()

    Checkpoint 3 of 5· Check yourself

    Which call returns the same rows as df.distinct()?

    Checkpoint 4 of 5· Exam question

    A data engineer loads a bronze table of clickstream events where upstream retries occasionally write the exact same row multiple times across all columns. Before writing to silver, the engineer wants to remove full-row duplicates while keeping the rest of the pipeline unchanged. Which transformation should the engineer apply to the DataFrame?

    Sources2

    3.dropDuplicates on streaming DataFrames: state and watermarks

    The same method behaves differently depending on the kind of DataFrame. On a static batch DataFrame, all the rows are available at once, so dropDuplicates just removes the duplicates and finishes. A streaming DataFrame never has all of its data at once. A duplicate of a record seen in an earlier trigger could arrive in a later one. To catch it, Spark must remember what it has already seen.

    The documentation answers this directly. For a streaming DataFrame, dropDuplicates keeps all data across triggers as intermediate state, so with no limit that state keeps growing. The control for this is withWatermark. A watermark sets how late duplicate data can be, and the system limits the state to match. There is a cost: data older than the watermark is dropped, so a very late duplicate can't slip through as a new row.

    Checkpoint 5 of 5· Check yourself

    A streaming job deduplicates events with dropDuplicates and its state keeps growing. What does the documentation recommend to bound the state?

    Sources2

    Exam traps

    Each one states something that sounds right. Open it to see what is actually true.

    1. 1.distinct() can be given a list of key columns so that it deduplicates on those columns only.Why is that wrong?

      distinct() takes no parameters and always compares whole rows. To deduplicate on chosen columns, use dropDuplicates with a subset.

      Covered in distinct(): removing rows that match in every column

    2. 2.dropDuplicates works the same way on a streaming DataFrame as on a batch DataFrame, at no extra cost.Why is that wrong?

      On a streaming DataFrame it keeps data across triggers as intermediate state. You need withWatermark to bound that state, and data older than the watermark is dropped.

      Covered in dropDuplicates on streaming DataFrames: state and watermarks

    Sources

    Every claim above is drawn from one of these pages, quoted as it was written on the date shown.

    1. 1.
    2. 2.
      “List of columns to use for duplicate comparison (default All columns).”
      ↩︎ dropDuplicates(subset): deduplicating on key columns
      “In addition, data older than watermark will be dropped to avoid any possibility of duplicates.”
      ↩︎ dropDuplicates on streaming DataFrames: state and watermarks
      “List of columns to use for duplicate comparison (default All columns).”
      ↩︎ Key concept
      “Return a new DataFrame with duplicate rows removed, optionally only considering certain columns.”
      ↩︎ Exam trap 1
      “For a streaming DataFrame, it will keep all data across triggers as intermediate state to drop duplicates rows.”
      ↩︎ Exam trap 2
      “Return a new DataFrame with duplicate rows removed, optionally only considering certain columns.”
      ↩︎ Prediction
      “You can use withWatermark to limit how late the duplicate data can be and the system will accordingly limit the state.”
      ↩︎ Checkpoint

    Continue to page 2 of 2

    PySpark Aggregations: count, approx_count_distinct, mean and summary

    Spotted a mistake, or was something unclear? Tell us.