Troubleshooting Join Issues - Tableau

  1. Which conclusion is best supported by these results?
    Answer: Some orders match multiple status rows, but the left join has retained every original OrderID. When you see row counts change after a join, your first instinct should be to ask why — specifically, whether rows were gained, lost, or duplicated. Here, the left table has 80,00080{,}000 rows and the joined result has 126,000126{,}000, meaning 46,00046{,}000 extra rows appeared. That's the key signal. A left join never drops rows from the left table — it always preserves every left-side record. So those 46,00046{,}000 additional rows came from the right (status) table matching multiple times against the same OrderID values. Meanwhile, COUNTD(OrderID) = 80{,}000 confirms every original order is still present and no new OrderIDs were introduced. This makes C the correct conclusion: some orders matched multiple status rows, but every original OrderID survived the join intact. A is wrong because the passage explicitly states the left table has exactly one row per OrderID — no hidden duplicates existed before the join. B misreads the math; 46,00046{,}000 is the number of extra rows created by multi-row matches, not the count of unmatched orders. Unmatched orders would appear as single rows with null status values, not add rows. D contradicts both what left joins do (they never remove left-side rows) and what COUNTD confirms — no OrderIDs were lost or replaced. A useful rule of thumb: if post-join row count exceeds the left table, suspect one-to-many matches on the right side. If it equals the left table, it's one-to-one (or unmatched rows filling with nulls). COUNTD on your join key is your best diagnostic to confirm nothing was lost.
  2. Which explanation and corrective action are most appropriate?
    Answer: All null DeviceID rows on both sides can match one another; exclude null-key records from the join or replace them with genuinely discriminating key values. When Tableau's "match null join values" option is enabled, it treats NULL as equal to NULL during the join. This sounds helpful, but it creates a serious cardinality explosion — and that's exactly what this question tests. Here's why: in standard SQL, NULL ≠ NULL, so null-key rows never match. But with this option enabled, every null DeviceID row on the left matches every null DeviceID row on the right. If the left table has 1,000 null rows and the right has 1,000 null rows, you suddenly get 1,000,000 joined rows — a classic cross-join effect. The extract balloons because of this many-to-many null matching behavior. Answer A correctly identifies this root cause and prescribes the right fix: either filter out null-key records before the join or replace nulls with meaningful, discriminating values that join properly. Answer B is wrong because Tableau doesn't select a single arbitrary row per null — it matches all nulls against all nulls. There is no "one deterministic row" behavior, and sorting changes nothing about this. Answer C is wrong because no internal union is occurring. The growth comes from join row multiplication, not appending. Switching from left outer to inner join would not resolve a null matching explosion. Answer D is wrong because Tableau does not consolidate null-key rows into a single placeholder. Aggregating after the join wouldn't fix the underlying data explosion — it would just mask it. As a study tip: whenever you see "extract size increases dramatically" in a join scenario, immediately think about cardinality — specifically whether a many-to-many relationship has been accidentally created.
  3. Which correction best matches the intended data structure?
    Answer: Union the files so transactions are appended, then identify the year from the source or date. When you see two files with the same structure representing different time periods, the critical question is: do you want to combine rows side by side, or stack rows on top of each other? A join combines rows horizontally by matching keys — but when each customer has multiple transactions in both files, a join creates a Cartesian product. For example, a customer with 3 prior-year rows and 4 current-year rows produces 12 combined rows, inflating your data and distorting any analysis. The right fix is a Union, which appends rows vertically. D is correct because unioning the two files preserves every original transaction as its own row, and you can distinguish the year using either a source field Tableau automatically generates or a date column already in the data. This matches the actual structure: a single list of transactions across both years. A is tempting but wrong — aggregating by CustomerID before joining collapses all transaction-level detail, so you'd lose the granularity that makes transaction data useful in the first place. B misunderstands what join types control: left, right, and inner joins determine which matching keys are kept, not how many rows result from a multi-row match. The Cartesian inflation still occurs with a left join. C attempts to reduce the explosion by adding a second join condition, but transaction amounts aren't meaningful pairing logic — this creates arbitrary matches and still doesn't reflect the true data structure. As a study tip, train yourself to ask: "Am I combining columns, or stacking rows?" Joins = columns side by side; Unions = rows stacked. When files share identical columns across time periods, a Union is almost always the right tool.
  4. What is the most appropriate way to troubleshoot and correct this duplication?
    Answer: Match each transaction to the customer-history row whose effective date range contains the transaction date. When you see a question about joining tables that change over time — like customer segments, pricing tiers, or account statuses — you're dealing with a slowly changing dimension (SCD). The core challenge is matching each transaction not just to the right customer, but to the right version of that customer's record at the time of the transaction. A simple join on CustomerID alone ignores time entirely. If a customer has three historical segment rows, every transaction multiplies into three rows — one per segment record — causing the duplication described in the passage. The fix is a date-range join: match each transaction to the customer-history row where the transaction date falls between the effective start and end dates. This is exactly what A does, and it's the only approach that correctly aligns each transaction with its historically accurate segment. B misses the point entirely — an inner join removes unmatched rows but does nothing to resolve the one-to-many relationship causing duplication. You'd still get inflated records for customers with multiple history rows. C sounds reasonable but is fundamentally wrong for historical analysis; keeping only the most recent segment row would misclassify past transactions that occurred under a different segment. That corrupts your data rather than fixing it. D is the worst option — hiding fields in Tableau doesn't fix the underlying duplicate rows in the data source. Your aggregates (sums, counts) would still be inflated; you'd just be hiding the evidence. As a study tip: whenever a question involves a dimension table that stores history with date ranges, your instinct should be "date-range join," not a simple key match.
  5. What is the primary structural cause of the six-row result?
    Answer: The two child tables form a fanout because their rows are independently matched through the same order key. When two child tables are both joined to the same parent, you need to think about what happens structurally at the row level — this question is testing your understanding of join fanout (also called a many-to-many multiplication effect). Here's the core logic: Orders is the parent. OrderLines adds 3 rows for the order; Returns adds 2 rows. When both are joined to Orders via OrderID, the database has no natural way to pair the child rows with each other — so every OrderLines row gets matched with every Returns row, producing 3×2=63 \times 2 = 6 rows. This is fanout: two independent child relationships intersecting through the same key create a Cartesian-style multiplication. Answer A correctly names this — the child tables are independently matched through the same parent key, which is precisely what causes the multiplication. B is wrong because it describes an unmatched (null-preserving) record, which is a concept from outer joins, not from the duplication caused by two populated child tables multiplying against each other. C is wrong because Tableau does not automatically union child rows. A union stacks rows vertically; it doesn't govern how two separate joins interact. This answer confuses union with join behavior. D is wrong on two counts: inner joins do not preserve all rows from both inputs (that's an outer join), and duplication here stems from the two-child fanout structure, not from a property of inner joins alone. Study tip: Whenever you see multiple child tables joined to the same parent, immediately ask yourself whether fanout could inflate row counts — this is one of the most common data modeling pitfalls tested on the Tableau exam.
  6. Which data-modeling change best preserves both analytical requirements without relying on a view-specific workaround?
    Answer: Relate Sales and Quotas as logical tables using Salesperson and Month. When you see a question about fan traps or duplicated measures caused by joins, think about Tableau's logical layer vs. physical layer distinction. The core problem here is that joining Sales and Quotas physically causes quota values to repeat across every transaction row, making SUM(Quota) overcount dramatically. The cleanest fix is C — relating Sales and Quotas as logical tables. Tableau's relationship model keeps the two tables separate at the physical level and generates context-aware queries depending on what fields are on the viz. When you analyze transactions, Tableau queries Sales. When you analyze quotas, it queries Quotas. When you need both, it federates them correctly using the Salesperson and Month keys — no duplication, no workaround required. Each distractor has a specific flaw worth understanding. A attempts to correct overcounting with AVG(Quota), which works only when every salesperson has the same number of daily transactions each month — a fragile assumption that breaks with real, uneven data. B introduces a nonsensical join condition: Quotas has no daily date field, so joining on Transaction Date would either fail or create a massive cross-join, worsening the fan trap rather than solving it. D switches to a full outer join, which changes which rows are included (preserving unmatched rows from both sides) but does nothing to prevent quota duplication on matched rows — the overcounting problem remains entirely intact. Study tip: Whenever a question describes a fact table joined to an aggregate/summary table, that's your signal that a Tableau relationship (not a join) is likely the correct modeling choice. Relationships are designed precisely for this many-to-one mismatch scenario.
  7. Which change most directly corrects the row explosion while retaining the applicable regional price?
    Answer: Add Region to the join so both ProductCode and Region must match. When you see a question about unexpected row multiplication after a join, your first instinct should be to examine the granularity of the join condition — specifically, whether the join keys fully define a unique match between tables. Here, the Price table is structured at the ProductCode–Region grain, meaning each ProductCode appears three times (once per region). Joining on ProductCode alone tells Tableau to match every sales row to all three regional price rows, tripling your row count. The fix is to make the join condition as specific as the data: joining on both ProductCode and Region ensures each sales transaction matches exactly one price row — the correct regional price. That's why B is correct. A is wrong because switching from an inner join to a left join changes which rows are kept, not how many matches are made per row. A left join on ProductCode alone still produces three matches per transaction; you'd just retain unmatched sales rows too. C is wrong because the duplication isn't caused by duplicate ProductCodes in Sales — it's caused by multiple rows in the Price table sharing the same ProductCode across regions. Removing data from Sales doesn't fix the structural mismatch in the join condition. D is wrong because aggregating after the join collapses the inflated rows after the damage is done. You'd likely mix or average prices across regions rather than retrieve the applicable one, producing incorrect metrics rather than correcting the join logic. The key pattern: row explosion in joins always points to an incomplete join key. Ask yourself whether your join condition matches the true grain of the lookup table.
  8. What result should be expected if the analyst makes only that change?
    Answer: Matched accounts will still repeat, while accounts without contacts will be removed. When working with join types in Tableau, the key distinction to understand is that the join type controls which rows are included, not whether duplicates are eliminated. Duplicates arise from the one-to-many relationship between tables — and changing from a left join to an inner join does nothing to resolve that structural issue. Here's why: in a left join from Accounts to Contacts, every account appears at least once, and accounts with multiple contacts repeat once per contact row. An inner join simply removes accounts that have no matching contacts — it still preserves every matched row, including all the duplicates. So if an account has three contacts, it still appears three times. Answer A captures this precisely: matched accounts continue to repeat, but unmatched accounts (those with no contacts) are now excluded entirely rather than appearing with null contact values. Answer B is wrong because it reverses the logic — inner joins don't make matched accounts unique; they only drop unmatched ones. Nulls don't "remain" for unmatched accounts because those rows are removed entirely. Answer C is a common misconception: inner joins do not magically select one representative contact row per account. They return all valid matches. Answer D is wrong because Tableau doesn't automatically aggregate measures based on join type — aggregation is controlled separately through your view's level of detail and calculated fields. As a study tip, always separate two distinct problems: which rows are included (controlled by join type) versus duplicate row inflation (caused by data granularity). Changing the join type solves the first problem, not the second.
  9. What most likely caused the increase?
    Answer: The uppercase calculation collapsed distinct codes to one value, creating many-to-many matches within that value. When a join uses a calculated field like UPPER(CustomerCode), you need to think carefully about how that transformation affects the cardinality of your match — specifically, how many rows on one side can match how many rows on the other. Here's what happens in this scenario: System A has two distinct codes, ab and AB. System B also has two distinct rows, one for ab and one for AB. Before the UPPER() fix, these codes fail to match across systems (case mismatch). After applying UPPER() on both sides, both ab and AB collapse to the single value AB. Now every row in System A that evaluates to AB can match every row in System B that also evaluates to AB. That's a many-to-many join: 2 rows × 2 rows = 4 matched rows instead of 0 or 1. This fan-out is what causes the sharp row count increase, making D the correct answer. A is wrong because UPPER() has no effect on join type — it cannot convert an inner join into a left outer join. Join type is a separate configuration. B is wrong because UPPER() is a row-level transformation, not a structural operation like UNION, which stacks datasets vertically. C describes a real Tableau behavior where null keys match other nulls, but UPPER() on a non-null string never produces null — it simply uppercases the value. As a study tip, whenever you see a normalization function (UPPER, LOWER, TRIM) applied to a join key, immediately ask yourself: "Could this collapse multiple distinct values into one, creating a many-to-many relationship?" That fan-out is one of the most common sources of unexpected row inflation in Tableau joins.
  10. Before accepting this as a reliable correction, what should the developer verify?
    Answer: Every ProductID has exactly one active dimension row at the join's effective level of detail. Whenever a dimension table has multiple rows per key, your first concern should be granularity: does the join produce one-to-one or one-to-many matches? If a single ProductID maps to more than one dimension row at join time, every matching fact row gets duplicated — inflating aggregates silently and dangerously. Filtering to Status = Active feels like a clean fix, but it only works if each ProductID has exactly one active row. That's what answer B is asking you to confirm. If two active rows exist for the same ProductID (perhaps an overlapping effective-date range or a data quality issue), the duplication problem survives the filter. The join still fans out, and your sales totals are still wrong. Verifying one-active-row-per-ProductID is the only way to trust that the filter genuinely resolves the granularity mismatch. Answer A is a distraction — whether inactive rows have matching fact records is irrelevant to whether the active filter eliminates fan-out on the rows you kept. Answer C conflates data governance (transparency to end users) with data correctness; even if users could see the filter, that doesn't validate that the data is accurate. Answer D misunderstands the problem entirely — switching to a full outer join would introduce more unmatched rows, not resolve duplication; outer joins don't fix granularity issues caused by duplicate dimension keys. The study tip here: always trace the grain. Before trusting any filter as a duplication fix, ask yourself "after filtering, is there still a one-to-one relationship between dimension key and fact row?" If not, the filter is cosmetic, not corrective.

Start this quiz