<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Re: how to know which join type was used (broadcast, shuffle hash or sort merge join) for a query? in Data Engineering</title>
    <link>https://community.databricks.com/t5/data-engineering/how-to-know-which-join-type-was-used-broadcast-shuffle-hash-or/m-p/139569#M51234</link>
    <description>&lt;P&gt;Hello&amp;nbsp;&lt;a href="https://community.databricks.com/t5/user/viewprofilepage/user-id/147736"&gt;@smoortema&lt;/a&gt;&amp;nbsp;, here are some helpful tips and tricks.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;DIV class="paragraph"&gt;Here’s how to quickly determine which join strategy Spark used—between broadcast hash join, shuffle hash join, and sort-merge join—and how to read both the query plan and the Spark UI to verify it.&lt;/DIV&gt;
&lt;DIV class="paragraph"&gt;&amp;nbsp;&lt;/DIV&gt;
&lt;H3 class="paragraph"&gt;Quick answers&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;The easiest way: run SQL EXPLAIN or DataFrame.explain to see the initial physical plan; look for operator names like BroadcastHashJoin, ShuffleHashJoin, or SortMergeJoin in the plan output.&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;To see what was actually executed (especially with AQE enabled), use the Spark UI’s SQL tab. The diagram shows the current/final plan; join nodes are labeled and include metrics (rows output, shuffle read/write, broadcast size).&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;With AQE, the initial plan shown by EXPLAIN may differ from the executed plan; the Spark UI reflects dynamic changes (e.g., SMJ converted to BHJ at runtime).&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;How to tell from the query plan&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;Use SQL EXPLAIN or DataFrame.explain to inspect the physical plan before execution; scan for join nodes: * BroadcastHashJoin → broadcast hash join. * ShuffleHashJoin → shuffle hash join. * SortMergeJoin → sort-merge join.&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;EXPLAIN always shows the initial plan and does not reflect AQE re-optimizations; compare EXPLAIN output with the Spark UI to see if AQE changed the join at runtime.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;In Databricks, AQE can dynamically change a planned sort-merge join into a broadcast hash join if a join side is under the adaptive broadcast threshold (default 30MB). Look for different join nodes between initial and current/final plans to confirm the change.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;If using Photon, you may see Photon-specific operators (e.g., PhotonBroadcastHashJoin); this indicates Photon executed that part of the plan.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;How to tell from the Spark UI&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;Open the SQL tab → select the query → view the DAG/plan diagram. Join operators are labeled directly: * BroadcastHashJoin for BHJ. * ShuffleHashJoin for SHJ. * SortMergeJoin for SMJ.&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;Hover or expand join nodes to see metrics:
&lt;UL&gt;
&lt;LI&gt;Rows output can reveal “row explosion” (unexpectedly high output cardinality).&lt;/LI&gt;
&lt;LI&gt;Shuffle read/write shows how much data moved for SHJ/SMJ.&lt;/LI&gt;
&lt;LI&gt;Broadcast size appears for BHJ stages and helps confirm broadcast happened.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;With AQE, the plan diagram can evolve during execution; the Spark UI shows the current/final executed plan, not the initial plan. Use it to verify runtime strategy changes (e.g., SMJ → BHJ) and optimizations like partition coalescing or skew handling via CustomShuffleReader annotations (coalesced/skewed).&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;Notes about AQE (Adaptive Query Execution)&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;AQE may switch sort-merge join to broadcast hash join at runtime based on accurate post-shuffle statistics; the threshold for dynamic switch is spark.databricks.adaptive.autoBroadcastJoinThreshold (default 30MB).&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;EXPLAIN does not execute the query, so it shows the initial plan only; the Spark UI shows the plan as it evolves and the final executed plan, making it the authoritative source for what actually ran under AQE.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;AQE also handles skew in SMJ/SHJ by splitting skewed partitions; you’ll see indicators like SortMergeJoin with isSkew=true and CustomShuffleReader with skewed in the plan/UI.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;Forcing or controlling join types (when needed)&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;Use join hints to request a strategy:
&lt;UL&gt;
&lt;LI class="paragraph"&gt;BROADCAST(table) → broadcast hash join.&lt;/LI&gt;
&lt;LI class="paragraph"&gt;MERGE(table) or SHUFFLE_MERGE(table) → sort-merge join.&lt;/LI&gt;
&lt;LI class="paragraph"&gt;SHUFFLE_HASH(table) → shuffle hash join.&lt;/LI&gt;
&lt;LI class="paragraph"&gt;Spark prioritizes hints: BROADCAST over MERGE over SHUFFLE_HASH over SHUFFLE_REPLICATE_NL; not all strategies support all join types.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;Key configs:
&lt;UL&gt;
&lt;LI&gt;spark.sql.autoBroadcastJoinThreshold controls static broadcast planning (Spark typically broadcasts small tables by default; many workloads start around 10MB, adjustable).&lt;/LI&gt;
&lt;LI&gt;spark.databricks.adaptive.autoBroadcastJoinThreshold controls AQE’s dynamic switch to BHJ at runtime (default 30MB).&lt;/LI&gt;
&lt;LI&gt;spark.sql.join.preferSortMergeJoin (true by default) can be set to false to prefer SHJ where feasible; Photon similarly tends to favor SHJ to speed up queries.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;Even with AQE enabled, broadcast hints can still outperform a dynamic conversion because AQE may only decide to broadcast after both sides shuffle; hints avoid that shuffle upfront if you know a side is small.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;Practical checklist&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;Before running:
&lt;UL&gt;
&lt;LI class="paragraph"&gt;EXPLAIN your query; confirm the planned join node names match expectations (BHJ/SHJ/SMJ).&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;After running:
&lt;UL&gt;
&lt;LI&gt;Spark UI → SQL tab → check the join node label and metrics to see what actually executed and whether AQE changed it.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;If the executed plan isn’t the one you want:
&lt;UL&gt;
&lt;LI&gt;Consider adding a join hint or adjusting configs (autoBroadcastJoinThreshold, preferSortMergeJoin) and rerun; verify again in EXPLAIN and the Spark UI.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;Useful references&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;Adaptive Query Execution user guide (plans, Spark UI behavior, configs, dynamic BHJ conversion).&lt;/LI&gt;
&lt;LI class="paragraph"&gt;Join hints syntax and priority (BROADCST/MERGE/SHUFFLE_HASH/SHUFFLE_REPLICATE_NL).&lt;/LI&gt;
&lt;LI class="paragraph"&gt;Best practices for choosing BHJ vs SMJ vs SHJ and reading Spark UI join metrics.&lt;/LI&gt;
&lt;LI class="paragraph"&gt;AQE blog posts (identifying strategy changes and CustomShuffleReader coalesce/skew indicators).&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Hope this helps, Louis.&lt;/P&gt;
&lt;DIV class="paragraph"&gt;&amp;nbsp;&lt;/DIV&gt;</description>
    <pubDate>Tue, 18 Nov 2025 20:47:19 GMT</pubDate>
    <dc:creator>Louis_Frolio</dc:creator>
    <dc:date>2025-11-18T20:47:19Z</dc:date>
    <item>
      <title>how to know which join type was used (broadcast, shuffle hash or sort merge join) for a query?</title>
      <link>https://community.databricks.com/t5/data-engineering/how-to-know-which-join-type-was-used-broadcast-shuffle-hash-or/m-p/139565#M51232</link>
      <description>&lt;P&gt;What is the best way to know what kind of join was used for a SQL query between broadcast, shuffle hash and sort merge? How can the spark UI or the query plan be interpreted?&lt;/P&gt;</description>
      <pubDate>Tue, 18 Nov 2025 19:54:25 GMT</pubDate>
      <guid>https://community.databricks.com/t5/data-engineering/how-to-know-which-join-type-was-used-broadcast-shuffle-hash-or/m-p/139565#M51232</guid>
      <dc:creator>smoortema</dc:creator>
      <dc:date>2025-11-18T19:54:25Z</dc:date>
    </item>
    <item>
      <title>Re: how to know which join type was used (broadcast, shuffle hash or sort merge join) for a query?</title>
      <link>https://community.databricks.com/t5/data-engineering/how-to-know-which-join-type-was-used-broadcast-shuffle-hash-or/m-p/139569#M51234</link>
      <description>&lt;P&gt;Hello&amp;nbsp;&lt;a href="https://community.databricks.com/t5/user/viewprofilepage/user-id/147736"&gt;@smoortema&lt;/a&gt;&amp;nbsp;, here are some helpful tips and tricks.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;DIV class="paragraph"&gt;Here’s how to quickly determine which join strategy Spark used—between broadcast hash join, shuffle hash join, and sort-merge join—and how to read both the query plan and the Spark UI to verify it.&lt;/DIV&gt;
&lt;DIV class="paragraph"&gt;&amp;nbsp;&lt;/DIV&gt;
&lt;H3 class="paragraph"&gt;Quick answers&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;The easiest way: run SQL EXPLAIN or DataFrame.explain to see the initial physical plan; look for operator names like BroadcastHashJoin, ShuffleHashJoin, or SortMergeJoin in the plan output.&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;To see what was actually executed (especially with AQE enabled), use the Spark UI’s SQL tab. The diagram shows the current/final plan; join nodes are labeled and include metrics (rows output, shuffle read/write, broadcast size).&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;With AQE, the initial plan shown by EXPLAIN may differ from the executed plan; the Spark UI reflects dynamic changes (e.g., SMJ converted to BHJ at runtime).&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;How to tell from the query plan&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;Use SQL EXPLAIN or DataFrame.explain to inspect the physical plan before execution; scan for join nodes: * BroadcastHashJoin → broadcast hash join. * ShuffleHashJoin → shuffle hash join. * SortMergeJoin → sort-merge join.&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;EXPLAIN always shows the initial plan and does not reflect AQE re-optimizations; compare EXPLAIN output with the Spark UI to see if AQE changed the join at runtime.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;In Databricks, AQE can dynamically change a planned sort-merge join into a broadcast hash join if a join side is under the adaptive broadcast threshold (default 30MB). Look for different join nodes between initial and current/final plans to confirm the change.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;If using Photon, you may see Photon-specific operators (e.g., PhotonBroadcastHashJoin); this indicates Photon executed that part of the plan.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;How to tell from the Spark UI&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;Open the SQL tab → select the query → view the DAG/plan diagram. Join operators are labeled directly: * BroadcastHashJoin for BHJ. * ShuffleHashJoin for SHJ. * SortMergeJoin for SMJ.&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;Hover or expand join nodes to see metrics:
&lt;UL&gt;
&lt;LI&gt;Rows output can reveal “row explosion” (unexpectedly high output cardinality).&lt;/LI&gt;
&lt;LI&gt;Shuffle read/write shows how much data moved for SHJ/SMJ.&lt;/LI&gt;
&lt;LI&gt;Broadcast size appears for BHJ stages and helps confirm broadcast happened.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;With AQE, the plan diagram can evolve during execution; the Spark UI shows the current/final executed plan, not the initial plan. Use it to verify runtime strategy changes (e.g., SMJ → BHJ) and optimizations like partition coalescing or skew handling via CustomShuffleReader annotations (coalesced/skewed).&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;Notes about AQE (Adaptive Query Execution)&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;AQE may switch sort-merge join to broadcast hash join at runtime based on accurate post-shuffle statistics; the threshold for dynamic switch is spark.databricks.adaptive.autoBroadcastJoinThreshold (default 30MB).&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;EXPLAIN does not execute the query, so it shows the initial plan only; the Spark UI shows the plan as it evolves and the final executed plan, making it the authoritative source for what actually ran under AQE.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;AQE also handles skew in SMJ/SHJ by splitting skewed partitions; you’ll see indicators like SortMergeJoin with isSkew=true and CustomShuffleReader with skewed in the plan/UI.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;Forcing or controlling join types (when needed)&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;Use join hints to request a strategy:
&lt;UL&gt;
&lt;LI class="paragraph"&gt;BROADCAST(table) → broadcast hash join.&lt;/LI&gt;
&lt;LI class="paragraph"&gt;MERGE(table) or SHUFFLE_MERGE(table) → sort-merge join.&lt;/LI&gt;
&lt;LI class="paragraph"&gt;SHUFFLE_HASH(table) → shuffle hash join.&lt;/LI&gt;
&lt;LI class="paragraph"&gt;Spark prioritizes hints: BROADCAST over MERGE over SHUFFLE_HASH over SHUFFLE_REPLICATE_NL; not all strategies support all join types.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;Key configs:
&lt;UL&gt;
&lt;LI&gt;spark.sql.autoBroadcastJoinThreshold controls static broadcast planning (Spark typically broadcasts small tables by default; many workloads start around 10MB, adjustable).&lt;/LI&gt;
&lt;LI&gt;spark.databricks.adaptive.autoBroadcastJoinThreshold controls AQE’s dynamic switch to BHJ at runtime (default 30MB).&lt;/LI&gt;
&lt;LI&gt;spark.sql.join.preferSortMergeJoin (true by default) can be set to false to prefer SHJ where feasible; Photon similarly tends to favor SHJ to speed up queries.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;Even with AQE enabled, broadcast hints can still outperform a dynamic conversion because AQE may only decide to broadcast after both sides shuffle; hints avoid that shuffle upfront if you know a side is small.&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;Practical checklist&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;Before running:
&lt;UL&gt;
&lt;LI class="paragraph"&gt;EXPLAIN your query; confirm the planned join node names match expectations (BHJ/SHJ/SMJ).&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;After running:
&lt;UL&gt;
&lt;LI&gt;Spark UI → SQL tab → check the join node label and metrics to see what actually executed and whether AQE changed it.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/DIV&gt;
&lt;/LI&gt;
&lt;LI&gt;
&lt;DIV class="paragraph"&gt;If the executed plan isn’t the one you want:
&lt;UL&gt;
&lt;LI&gt;Consider adding a join hint or adjusting configs (autoBroadcastJoinThreshold, preferSortMergeJoin) and rerun; verify again in EXPLAIN and the Spark UI.&lt;/LI&gt;
&lt;/UL&gt;
&lt;/DIV&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;H3 class="paragraph"&gt;Useful references&lt;/H3&gt;
&lt;UL&gt;
&lt;LI class="paragraph"&gt;Adaptive Query Execution user guide (plans, Spark UI behavior, configs, dynamic BHJ conversion).&lt;/LI&gt;
&lt;LI class="paragraph"&gt;Join hints syntax and priority (BROADCST/MERGE/SHUFFLE_HASH/SHUFFLE_REPLICATE_NL).&lt;/LI&gt;
&lt;LI class="paragraph"&gt;Best practices for choosing BHJ vs SMJ vs SHJ and reading Spark UI join metrics.&lt;/LI&gt;
&lt;LI class="paragraph"&gt;AQE blog posts (identifying strategy changes and CustomShuffleReader coalesce/skew indicators).&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;Hope this helps, Louis.&lt;/P&gt;
&lt;DIV class="paragraph"&gt;&amp;nbsp;&lt;/DIV&gt;</description>
      <pubDate>Tue, 18 Nov 2025 20:47:19 GMT</pubDate>
      <guid>https://community.databricks.com/t5/data-engineering/how-to-know-which-join-type-was-used-broadcast-shuffle-hash-or/m-p/139569#M51234</guid>
      <dc:creator>Louis_Frolio</dc:creator>
      <dc:date>2025-11-18T20:47:19Z</dc:date>
    </item>
    <item>
      <title>Re: how to know which join type was used (broadcast, shuffle hash or sort merge join) for a query?</title>
      <link>https://community.databricks.com/t5/data-engineering/how-to-know-which-join-type-was-used-broadcast-shuffle-hash-or/m-p/139613#M51247</link>
      <description>&lt;P&gt;Thanks for the useful informations! I have two additional questions:&lt;/P&gt;&lt;P&gt;1. Your answer looks like it is LLM-generated. If it is, could you share which LLM you used for it?&lt;/P&gt;&lt;P&gt;2. What is the best way to find the query in Spark UI? I am getting there through Compute, then selecting the compute I used, then Spark UI. Here I can find many queries, and it is not always evident which one I am looking for. Also, I can only see the queries that happened since the last restart of the compute. So finding the query becomes especially hard once it has already completed. Is there an easier way to find the query I am looking for?&lt;/P&gt;</description>
      <pubDate>Wed, 19 Nov 2025 08:22:29 GMT</pubDate>
      <guid>https://community.databricks.com/t5/data-engineering/how-to-know-which-join-type-was-used-broadcast-shuffle-hash-or/m-p/139613#M51247</guid>
      <dc:creator>smoortema</dc:creator>
      <dc:date>2025-11-19T08:22:29Z</dc:date>
    </item>
    <item>
      <title>Re: how to know which join type was used (broadcast, shuffle hash or sort merge join) for a query?</title>
      <link>https://community.databricks.com/t5/data-engineering/how-to-know-which-join-type-was-used-broadcast-shuffle-hash-or/m-p/139680#M51267</link>
      <description>&lt;P&gt;&lt;a href="https://community.databricks.com/t5/user/viewprofilepage/user-id/147736"&gt;@smoortema&lt;/a&gt;&amp;nbsp;, Spark performance tuning is one of the hardest topics to teach or learn, and it’s even tougher to do justice to in a forum thread. That said, I’m really glad to see you asking the question. Tuning is challenging precisely because there are so many moving pieces, which is why AQE was introduced in the first place — to take a large portion of that burden off your shoulders.&lt;/P&gt;
&lt;P class="p1"&gt;If you want to go deeper, structured training is your best path. Databricks offers courses that walk through tuning concepts step by step, and I’m sure platforms like Udemy have solid options as well. A guided approach will give you the most clarity and confidence as you level up your skills.&lt;/P&gt;
&lt;P class="p1"&gt;Hope this helps, Louis.&lt;/P&gt;</description>
      <pubDate>Wed, 19 Nov 2025 13:38:16 GMT</pubDate>
      <guid>https://community.databricks.com/t5/data-engineering/how-to-know-which-join-type-was-used-broadcast-shuffle-hash-or/m-p/139680#M51267</guid>
      <dc:creator>Louis_Frolio</dc:creator>
      <dc:date>2025-11-19T13:38:16Z</dc:date>
    </item>
  </channel>
</rss>

