Validating Row Counts - SQL
- What row count should an inner join from the fact table to the dimension table produce? Answer: rows, because the duplicated key adds two extra rows per referencing fact row. Whenever you see an inner join involving duplicate keys, your job is to think about the Cartesian product formed between matching rows — not just which rows survive. An inner join keeps only rows where the join condition matches, but when a key appears multiple times on one side, each match on the left pairs with every matching row on the right. Here, key
Kappears 3 times in the dimension and 15 times in the fact table. Those 15 fact rows don't each find one match — they each find 3 matches, producing rows from keyKalone. The remaining fact rows each match exactly one dimension row, contributing rows. The total is , confirming C is correct. A is wrong because it assumes only unmatched rows are dropped and ignores the duplication effect — it would be correct only if every dimension key were unique. B claims the duplicated key adds one extra row per referencing fact row (i.e., extra, giving ), but this confuses "2 copies" with "3 copies" — three dimension rows forKmeans two extras per fact row, not one. D misreads the duplication entirely, treating each duplicated dimension row as adding a full set of 15 rows rather than multiplying the existing matches. The key study tip: always compute join output as . Duplicate keys multiply rows — they don't just add them linearly. - If the posted-status predicate is placed in the
ONclause of a left join, what row count should be expected?Answer: rows, because posted transactions remain and accounts without posted transactions add null-extended rows. When you place a filter condition in theONclause of aLEFT JOINrather than theWHEREclause, the behavior changes fundamentally: the condition controls which rows match, but the left table still retains every row. Unmatched left-table rows simply receiveNULLfor the right-table columns. This is the core concept being tested here. With that in mind, here's the math: accounts have at least one posted transaction, producing matched rows. The remaining accounts find no posted match under theONpredicate, so they appear as null-extended rows. That gives you total rows — confirming B is correct. A is wrong because it treats the left join like an inner join. Filtering inONdoes not eliminate unmatched left-table rows; it only determines what gets joined on the right side. Those accounts still show up. C () has no logical basis. You don't simply add the account total () to the posted-transaction count () minus some overlap — that arithmetic doesn't reflect how join output is computed. D () would imply every account row appears plus every transaction row independently, which describes neither a join nor any standard SQL operation. It confuses row-level joining with set union. A helpful rule of thumb: moving a predicate fromWHEREtoONin a left join "softens" the filter — it becomes a join condition rather than a post-join exclusion, preserving all left-side rows. - Which validation most appropriately determines whether is the correct row count? Answer: For each order, multiply its payment count by its shipment count, then sum those products. When you join a parent table to multiple child tables simultaneously, you're dealing with a fan-out multiplication problem. Each order row gets duplicated once per matching payment and once per matching shipment — and these two expansions multiply together, not add together. Understanding this is the key to validating multi-table join results. The correct approach is D. For any given order, if it has payments and shipments, the join produces rows for that order — every payment gets paired with every shipment. The true expected row count is , summed across all orders. This is the only formula that reflects how relational joins actually behave when two child tables are joined to the same parent. Choice A is wrong because assumes each child row appears exactly once, independently. It ignores the multiplicative fan-out — payments and shipments are cross-joined per order, not stacked. Choice B is wrong because the larger table (shipments, with 160 rows) does not "cap" anything. An order with 3 payments and 4 shipments contributes 12 rows, which exceeds either child table's individual contribution. Choice C is wrong because uses a subtraction heuristic that has no basis in join mechanics. It would only make sense if joins were additive, which they are not. As a study tip: whenever you see a parent joined to two child tables, always think multiplication per parent row, not addition. Sketch a small example (e.g., 1 order, 2 payments, 3 shipments = 6 rows) to build the intuition quickly.
- What is the most appropriate conclusion from the decrease at the second stage? Answer: A row-removing operation or noncomparable snapshot exists because a pure left join cannot reduce its left input. Whenever you encounter a question about row counts changing through a join sequence, your first move should be to recall the fundamental guarantee of a left join: it can never reduce the number of rows below the left input's count. Every row on the left side is preserved — either matched with a right-side row or padded with NULLs. This is the cornerstone of this question. Here, the intermediate result has rows entering the second stage. After a pure left join to table
C, the output must have at least rows — yet only remain. That mathematical impossibility is the signal. Something other than a plain left join occurred: perhaps a filter was applied afterward, the counts came from different pipeline runs or snapshots, or the join type was mischaracterized in documentation. Answer C correctly identifies this: a row-removing operation or a noncomparable snapshot must exist, because the observed behavior is structurally incompatible with a left join. Answer A is wrong because duplicate keys inCwould increase the row count, not decrease it — each left row could match multiple right rows, producing fans. Answer B is the most tempting distractor, but it describes inner join behavior. Unmatched rows in a left join produce NULL-filled columns, not dropped rows — the left side is always preserved. Answer D is partially grounded in SQL truth (NULLs don't match NULLs), but NULL join keys would simply produce unmatched rows, which a left join still retains as NULL-padded output — no rows would be lost. As a study tip: whenever a left join appears to reduce rows, treat it as a red flag that something undocumented — a filter, a DISTINCT, or a snapshot mismatch — is hiding in the pipeline. - What row count should be expected from
orders o LEFT JOIN line_items li ON o.order_id = li.order_id?Answer: rows, because line items produce rows and unmatched orders add rows. When working with JOIN operations, your job is to think row-by-row: for each row on the left table, how many matching rows exist on the right? That multiplying effect — not simple addition or subtraction — is what determines your final row count. In aLEFT JOIN, every row from the left table (orders) appears in the result at least once. For orders with line items, the order row is duplicated for each matching line item. For orders without matches, the order row still appears once, withNULLfilling the line-item columns. Here, orders have at least one line item, and those orders collectively account for all line-item rows. The remaining unmatched orders each contribute exactly oneNULL-padded row. So the total is rows, confirming C is correct. A is wrong because it assumes the result is capped at the line-item count. ALEFT JOINguarantees unmatched left-side rows still appear, so you cannot stop at . B describes simple addition of both table sizes (), which would only make sense if every order row were preserved independently and stacked — that's not how joins work. Matched rows merge, not stack. D () seems to subtract the unmatched orders from , which applies the wrong operation entirely and ignores how unmatched rows actually behave in a left join. A reliable tip: for any join question, ask "how many times does each left-side row appear?" — the answer is always equal to its match count (or if unmatched in aLEFT JOIN). - What counts should be returned, respectively, by an inner join, a left join, and a left anti-join implemented with
WHERE dimension.key IS NULL?Answer: , , and rows, respectively. When working with joins, you need to carefully track which rows qualify for each join type based on NULL handling and key matching. Start by breaking down the source rows: have a null foreign key, have a non-null foreign key that doesn't exist in the dimension, and match exactly one dimension row (). An inner join returns only rows where both sides match. Nulls never match anything, and unmatched non-null keys are excluded — so you get exactly rows. A left join preserves every source row regardless of whether it matches, so all rows are returned (unmatched rows simply get NULLs on the dimension side). A left anti-join usingWHERE dimension.key IS NULLfilters for rows where the dimension produced no match — that's the null-key rows plus the non-null unmatched rows, totaling . This confirms answer **A: , , and **. Choice B is wrong because it claims the anti-join returns — forgetting that null foreign keys also fail to match and appear in the result. Choice C is wrong on two counts: it incorrectly drops the left join count to (excluding the null-key rows, which a left join should still preserve) and gives only for the anti-join. Choice D invents a left join count of , which would require duplicate matches — impossible when the dimension key is unique. A useful rule of thumb: left join = all source rows; inner join = matched rows only; anti-join viaIS NULL= all unmatched source rows, including those with null foreign keys. - Which conclusion is supported by these counts? Answer: Exactly customers have no orders because those rows contain a null order identifier. When you perform a LEFT JOIN, every row in the result set represents either a matched or unmatched record from the left table. The key insight is how
COUNT(*)andCOUNT(column)behave differently:COUNT(*)counts every row regardless of nulls, whileCOUNT(column)skips rows where that column is null. Here,COUNT(*) = 260tells you the join produced 260 total rows.COUNT(o.order_id) = 260would mean every customer matched an order — but instead you getCOUNT(o.order_id) = 140. Sinceorder_idis never null in the original orders table, any nullorder_idin the result must come from an unmatched left-join row (a customer with no orders). That gives you customers without orders, confirming B is correct. A is wrong because the gap doesn't indicate multiple orders per customer — it indicates missing matches (nulls). You'd needCOUNT(*) > COUNT(DISTINCT customer_id)to detect duplicate rows from multiple orders. C is wrong because 140 non-nullorder_idvalues don't guarantee 140 distinct customers. A single customer with multiple orders would contribute multiple non-nullorder_idrows, so you'd needCOUNT(DISTINCT customer_id)among matched rows to be sure. D is wrong because you can derive the number of orderless customers directly — the null count from the LEFT JOIN gives you exactly that without any additional distinct counting. Study tip: Always distinguishCOUNT(*)fromCOUNT(column)in LEFT JOIN scenarios — the difference between them directly equals the number of unmatched left-table rows. - Which validation result most directly confirms that the omitted company key caused the row-count discrepancy? Answer: A join on both key columns returns rows, and the excess rows disappear. When diagnosing a join gone wrong, the gold standard is controlled comparison: isolate the variable you suspect, change it, and observe whether the problem disappears. That's exactly the logic this question tests. Here, joining only on
invoice_idproduces rows instead of the expected . The hypothesis is that omittingcompany_idcauses cross-matching — allocation rows incorrectly matching invoices from the wrong company but with the sameinvoice_id. To confirm this, you need evidence that adding the missing key resolves the inflation. Answer A is correct because it provides a direct before-and-after comparison. Joining on both(company_id, invoice_id)returns exactly rows — matching the allocation table's count — and the phantom rows vanish. This is causal confirmation: changing only the join condition eliminates the discrepancy, proving the omitted column was responsible. Answer B is a distractor. The fact that distinct allocation IDs still appear doesn't explain why rows were produced — it just confirms duplicates exist on the invoice side. It describes a symptom, not a cause-and-effect validation. Answer C simply restates the input row counts, which were already given in the problem setup. Knowing the tables have and rows before the join tells you nothing about why the join inflated the result. Answer D is a trap. A left join preserving rows only confirms that the extra rows aren't NULL-padded — it doesn't isolate the join key as the culprit. Study tip: When debugging join inflation, always test the fix directly — correcting the join condition and verifying the expected row count is the most airtight validation strategy. - Which conclusion is justified by these validation counts? Answer: Every source event is represented in the output, and at least one event produced more than one joined row due to fanout in the attributes table. When validating joins, two counts tell you different things:
COUNT(*)reveals the total output rows, whileCOUNT(DISTINCT event_id)tells you how many unique source records survived into the result. Learning to read these together is the core skill being tested here. The events table has rows, andCOUNT(DISTINCT event_id) = 10{,}000confirms every single source event appears in the join output — none were silently dropped. Meanwhile, `COUNT(*) = 10{,}25025010{,}00010{,}000$$ distinct IDs does not confirm the total count is correct — that's exactly what the discrepancy reveals. Study tip: Always pairCOUNT(*)withCOUNT(DISTINCT key)when auditing joins. The gap between them diagnoses fanout; equality between the distinct count and your source row count confirms retention. - How many rows should a full outer join on the key return? Answer: rows, using pairwise matches for key and unmatched rows for other keys. When working with JOIN operations, the key is understanding exactly how rows are matched and combined. A full outer join returns every pairwise combination of matching rows, plus any unmatched rows from either table padded with NULLs. Let's walk through the data. Table
Ahas: two rows with key , one row with key , one row with key . TableBhas: three rows with key , two rows with key . For key , the join creates every pairwise combination: matched rows. Key appears only inA— that's unmatched row. Key appears only inA— that's unmatched row. Key appears only inB— that's unmatched rows. Total: rows, confirming D is correct. A is wrong because using the "larger multiplicity" ( for key ) ignores the cross-product nature of joins — it's not a max operation. B is wrong because counting "one matched row per shared key" misunderstands how joins work; matching rows combine pairwise, not one-to-one. C is wrong because summing all input rows () would only be correct if every row were unmatched — it ignores the multiplication effect of the cross-product on key . A useful tip: whenever a join key has multiplicity in one table and in the other, always calculate matched rows for that key, then add unmatched rows separately.