John Serra
Back to Blog
AnalyticsBusiness OperationsData AuditreportingSmall Business Analyticsspreadsheets

Why Counting Rows Can Mislead You: Row Grain vs. Unit of Analysis

Counting spreadsheet rows only works when one row equals one unit of what your question measures. In a 12-row exam booking export, valid pass rates span 41.67%, 55.56%, 50.00%, and 62.50%—here is a 3-step spreadsheet audit to match numerator and denominator before dividing.

Counting spreadsheet rows only answers a question when one row equals one unit of what you’re measuring. Before you divide, decide what the question counts: bookings, tests sat, or people. Then make sure the numerator and the denominator both count that same thing.

In the exam booking export below, 5 of 12 bookings ended in a pass: 5 ÷ 12 = 41.67%. Only 9 were tests someone actually sat: 5 ÷ 9 = 55.56%. Of the 8 candidates, 5 have at least one recorded pass: 5 ÷ 8 = 62.50%. All three are correct answers to different questions.

Below is the 12-row booking export I made up for this article.

Row booking_id candidate_id booking_date status
2 B01 C01 2026-09-01 fail
3 B02 C02 2026-09-01 pass
4 B03 C03 2026-09-01 cancelled
5 B04 C01 2026-09-02 pass
6 B05 C04 2026-09-02 no_show
7 B06 C05 2026-09-02 fail
8 B07 C03 2026-09-03 pass
9 B08 C06 2026-09-03 pass
10 B09 C07 2026-09-03 fail
11 B10 C08 2026-09-03 cancelled
12 B11 C05 2026-09-04 fail
13 B12 C07 2026-09-04 pass

From that export, the question “What is our pass rate?” has four valid answers:

Question What it counts Calculation Pass rate
What share of all bookings ended in a pass? Bookings 5 ÷ 12 41.67%
What share of tests sat ended in a pass? Attended tests 5 ÷ 9 55.56%
Of candidates who sat a test, what share passed the first one they sat in this export? Attended candidates 3 ÷ 6 50.00%
Of all candidates, what share have at least one recorded pass in this export? Candidates 5 ÷ 8 62.50%

The last row counts 5 candidates with a pass, not 5 pass rows. Here the two numbers happen to match. If C07 had passed both tests, there would be 6 pass rows, but still 5 passing candidates, so that the rate would stay 5 ÷ 8.

Two Correct Numbers From the Same 12 Rows

Suppose two people bring this export to a Monday meeting. One says 41.7% of students are passing. The other says 62.5%. Neither made a math mistake. The first divided 5 passes by 12 booking rows, then called it a share of people.

That’s the gap between two ideas:

  • Row grain is what one row represents. It belongs to the data. Here, one row is one exam booking with its final status.
  • Unit of analysis is what the question counts. It belongs to the question. “What share of students pass?” counts people, so its denominator has to count people too.

When the two match, counting rows works. When they don’t, you have to count something else first.

One assumption sits under this export: booking_date is the scheduled exam date, and no candidate has two bookings on one day. That’s an adaptation. The Grain First practice version of this data uses the date each booking was made. I changed it here so each candidate’s first test is easy to find. In many real exports, a booking date records when the booking was made. There, order tests by an attendance timestamp or attempt number. The candidates are the 8 people with a test scheduled from September 1 to 4, 2026.

Step 1: Check What One Row Represents

Create a tab called Bookings. Put the headings booking_id, candidate_id, booking_date, and status in A1:D1, and the 12 bookings in A2:D13. Leave out the preview’s Row column. Type the dates as real dates. A date stored as text can look the same, but the checks treat it as an error.

Column E checks each row against the extract window, so type 2026-09-01 in N2 and 2026-09-04 in N3 now, also as real dates. Then copy E2 from the formula reference at the end and fill it down to E13.

E2 returns CHECK ROW for a blank cell, a status other than exactly pass, fail, cancelled, or no_show, or a date that isn’t a whole date inside the window. The window itself must fall between 2000-01-01 and 2100-01-01. It also flags IDs that aren’t uppercase or contain spaces, non-breaking spaces, or any of * ? ~ = < >, and a candidate booked twice on one date. It won’t catch every hidden character.

Next, enter F2:K2 and fill them down to row 13. F2 and G2 count how often each ID appears. They stay blank until all 12 rows say OK.

F2:

=IF(OR(NOT(EXACT(E2, "OK")), COUNTIF($E$2:$E$13, "OK")<12), "", SUMPRODUCT(--EXACT($A$2:$A$13, A2)))

G2:

=IF(OR(NOT(EXACT(E2, "OK")), COUNT($F$2:$F$13)<12), "", SUMPRODUCT(--EXACT($B$2:$B$13, B2)))

Column F shows 1 on every row, so booking_id is unique. Column G shows 2 on 8 rows, because C01, C03, C05, and C07 each booked twice. That rules out “one row equals one person.” A count can rule a key out, but can’t prove it right. A short extract can be unique by chance, so read the source system’s definition too.

“Duplicate” can mean two numbers here. Eight rows belong to candidates who booked twice, but only 4 rows are extra beyond each candidate’s first: 12 − 8 = 4. These are real rebookings and retakes. Don’t run Remove Duplicates on candidate_id in the raw table, or you’ll delete real exam results.

H2 to K2 turn the bookings into one answer per candidate:

Cell Marks a 1 when Column total
H2 It’s the candidate’s first row 8 candidates
I2 It’s the first row, and the candidate sat at least one test 6 attended
J2 It’s the first row, and the candidate has at least one pass 5 with a pass
K2 The row is a pass, and it was the candidate’s earliest test sat in this export 3 first-test passes

H2:

=IF(OR(NOT(EXACT(E2, "OK")), COUNT($G$2:$G$13)<12), "", IF(SUMPRODUCT(--EXACT($B$2:B2, B2))=1, 1, 0))

I2:

=IF(OR(NOT(EXACT(E2, "OK")), COUNT($H$2:$H$13)<12), "", IF(H2=0, 0, IF(SUMPRODUCT(--EXACT($B$2:$B$13, B2), --EXACT($D$2:$D$13, "pass"))+SUMPRODUCT(--EXACT($B$2:$B$13, B2), --EXACT($D$2:$D$13, "fail"))>0, 1, 0)))

J2:

=IF(OR(NOT(EXACT(E2, "OK")), COUNT($I$2:$I$13)<12), "", IF(H2=0, 0, IF(SUMPRODUCT(--EXACT($B$2:$B$13, B2), --EXACT($D$2:$D$13, "pass"))>0, 1, 0)))

K2:

=IF(OR(NOT(EXACT(E2, "OK")), COUNT($J$2:$J$13)<12), "", IF(AND(EXACT(D2, "pass"), SUMPRODUCT(--EXACT($B$2:$B$13, B2), --EXACT($D$2:$D$13, "pass"), --($C$2:$C$13<=C2))+SUMPRODUCT(--EXACT($B$2:$B$13, B2), --EXACT($D$2:$D$13, "fail"), --($C$2:$C$13<=C2))=1), 1, 0))

K2 compares dates across the whole column, so sorting the rows doesn’t change the result. Swap the B01 and B04 rows, and column K still adds up to 3.

With all 12 bookings in place, columns E to K should show:

Row booking_id E F G H I J K
2 B01 OK 1 2 1 1 1 0
3 B02 OK 1 1 1 1 1 1
4 B03 OK 1 2 1 1 1 0
5 B04 OK 1 2 0 0 0 0
6 B05 OK 1 1 1 0 0 0
7 B06 OK 1 2 1 1 0 0
8 B07 OK 1 2 0 0 0 1
9 B08 OK 1 1 1 1 1 1
10 B09 OK 1 2 1 1 1 0
11 B10 OK 1 1 1 0 0 0
12 B11 OK 1 2 0 0 0 0
13 B12 OK 1 2 0 0 0 0
Total 12 OK 12 8 6 5 3
Google Sheets Bookings!A1:K13 table showing 12 exam bookings B01 through B12 across 8 candidates C01 through C08 from 2026-09-01 to 2026-09-04, with Row_Check OK in column E, Booking_ID_Freq 1 in column F, Candidate_ID_Freq 1 or 2 in column G, and distinct candidate indicator flags in columns H to K summing to 8 booked candidates, 6 attended candidates, 5 candidates with any pass, and 3 first-recorded attended passes.
Bookings!A1:K13 validates every booking row in column E, confirms unique booking IDs in column F alongside repeated candidate IDs in column G, and flags distinct candidates once in columns H to K. Open full-size image.

Step 2: Match Both Counts to the Question

Back to the four rates. The first two differ only in the denominator: 2 cancellations and 1 no-show were never sat. The third counts each attended candidate once, by their first test. C02, C03, and C06 passed their first recorded test in this export. C01 and C07 passed only on a retake, and C05 failed twice. The fourth also counts C04 and C08, who never sat a test.

A common worry is that retakes drag the booking rate down. A passed retake does the opposite. Without C01’s retake, B04, the table has 4 passes in 11 bookings: 4 ÷ 11 = 36.36%. Adding B04 raises the rate to 5 ÷ 12 = 41.67%. The booking rate gives repeat bookers more weight. It doesn’t punish a retake that passes.

This export covers September 1 to 4 only, so it can’t give a true first-attempt pass rate. C03 passed B07, but may have failed a test in August. It also can’t give a course-wide certification rate. That needs enrollment records, a defined follow-up window, and certification results, including students who never booked a test.

So which of the four is a good pass rate? None of them can be judged on its own. Before you compare any of them with another program’s number, find out which question that number answers.

Step 3: Reconcile the Counts and Check a Claimed Rate

I’ve seen business numbers that should agree drift apart across systems plenty of times. A pass rate copied into a slide is an easy place for it. This block checks a claimed rate against the booking table.

Put Metric, Verified_Extract_Benchmark, and Candidate_Claim_Audit in M1:O1, and the labels below in M2:M15. Type ALL_BOOKINGS in N4, and the starting values shown in O2:O13, with O2:O3 as real dates.

Row Label (M) N shows O starts as
2 Extract_Start_Date 2026-09-01 (typed) 2026-09-01
3 Extract_End_Date 2026-09-04 (typed) 2026-09-04
4 Claimed_Question_Scope ALL_BOOKINGS (typed) ALL_BOOKINGS
5 Invalid_Or_Blank_Row_Count 0 0
6 Total_Booking_Rows 12 NOT TESTED
7 Attended_Test_Count 9 NOT TESTED
8 Non_Attended_Booking_Count 3 NOT TESTED
9 Distinct_Booked_Candidates 8 NOT TESTED
10 Attended_Candidate_Count 6 NOT TESTED
11 Claim_Numerator_Count 5 NOT TESTED
12 Claim_Denominator_Count 12 NOT TESTED
13 Claim_Pass_Rate 0.4167 NOT TESTED
14 Exclusion_And_Rate_Gap 0 formula
15 Audit_Status RECONCILED: 12 BOOKINGS … formula

Enter these in column N:

Cell Formula
N5 =COUNTIF($E$2:$E$13, "<>OK")+COUNTBLANK($A$2:$A$13)+COUNTBLANK($B$2:$B$13)+COUNTBLANK($C$2:$C$13)+COUNTBLANK($D$2:$D$13)
N6 =IF(OR(N5>0, COUNT($F$2:$F$13)<12, COUNT($G$2:$G$13)<12, COUNT($H$2:$H$13)<12, COUNT($I$2:$I$13)<12, COUNT($J$2:$J$13)<12, COUNT($K$2:$K$13)<12), "CHECK INPUTS", COUNTA($A$2:$A$13))
N7 =IF(NOT(ISNUMBER(N6)), "CHECK INPUTS", SUMPRODUCT(--EXACT($D$2:$D$13, "pass"))+SUMPRODUCT(--EXACT($D$2:$D$13, "fail")))
N8 =IF(NOT(ISNUMBER(N6)), "CHECK INPUTS", SUMPRODUCT(--EXACT($D$2:$D$13, "cancelled"))+SUMPRODUCT(--EXACT($D$2:$D$13, "no_show")))
N9 =IF(NOT(ISNUMBER(N6)), "CHECK INPUTS", SUM($H$2:$H$13))
N10 =IF(NOT(ISNUMBER(N6)), "CHECK INPUTS", SUM($I$2:$I$13))
N13 =IF(OR(NOT(ISNUMBER(N6)), NOT(ISNUMBER(N11)), NOT(ISNUMBER(N12)), N12<=0), "CHECK INPUTS", ROUND(N11/N12, 4))
N14 =IF(NOT(ISNUMBER(N6)), "CHECK INPUTS", ROUND(ABS((N7+N8)-N6)+ABS(N6-ROWS($A$2:$A$13))+COUNTIF($F$2:$F$13, ">1"), 4))

N11 and N12 pick the numerator and denominator for the question named in O4.

N11:

=IF(NOT(ISNUMBER(N6)), "CHECK INPUTS", IF(EXACT(O4, "ALL_BOOKINGS"), SUMPRODUCT(--EXACT($D$2:$D$13, "pass")), IF(EXACT(O4, "ATTENDED_TESTS"), SUMPRODUCT(--EXACT($D$2:$D$13, "pass")), IF(EXACT(O4, "FIRST_ATTENDED_CANDIDATES"), SUM($K$2:$K$13), IF(EXACT(O4, "BOOKED_CANDIDATES_ANY_PASS"), SUM($J$2:$J$13), "CHECK SCOPE")))))

N12:

=IF(NOT(ISNUMBER(N6)), "CHECK INPUTS", IF(EXACT(O4, "ALL_BOOKINGS"), N6, IF(EXACT(O4, "ATTENDED_TESTS"), N7, IF(EXACT(O4, "FIRST_ATTENDED_CANDIDATES"), N10, IF(EXACT(O4, "BOOKED_CANDIDATES_ANY_PASS"), N9, "CHECK SCOPE")))))

N15 writes the status:

=IF(OR(N5>0, NOT(ISNUMBER(N6)), NOT(ISNUMBER(N13)), NOT(ISNUMBER(N14))), "STOP: CHECK SOURCE OR SCOPE", IF(COUNTIF($F$2:$F$13, ">1")>0, "STOP: DUPLICATE BOOKING IDS IN SOURCE", IF(N14<>0, "STOP: SOURCE EXCLUSION GAP", "RECONCILED: " & N6 & " BOOKINGS (" & N7 & " ATTENDED + " & N8 & " NON-ATTENDED) ACROSS " & N9 & " CANDIDATES")))

N5 is a stop signal, not a count of bad rows: a row with two blank cells adds 3. Anything above 0 means fix the source first. N6 waits until every helper cell in F:K holds a number. That catches a cleared formula, not one typed over with a number, so keep the formulas live. N14 should be 0: attended plus non-attended equals all bookings, and no booking_id repeats. N15 should read RECONCILED: 12 BOOKINGS (9 ATTENDED + 3 NON-ATTENDED) ACROSS 8 CANDIDATES.

Copy O14 and O15 from the formula reference. With O6:O13 still NOT TESTED, O15 says NOT TESTED: ENTER CANDIDATE CLAIM VALUES.

Now test each claim below. Type only into O4 and O6:O13, replacing all of them each time. Keep the dates in O2:O3 and 0 in O5. Every test uses O6 = 12, O7 = 9, O8 = 3, O9 = 8, and O10 = 6, except the last. Format N13, O13, and O14 to four decimals first, or Google Sheets shows 0.5 instead of 0.5000.

Claim O4 O11 O12 O13 O14 shows O15 shows
"41.7% of students are passing" BOOKED_CANDIDATES_ANY_PASS 5 12 0.4167 4.2083 STOP: ROW GRAIN MISMATCH (12 BOOKINGS USED FOR 8 CANDIDATES)
"55.56% of bookings passed: 5 ÷ 9" ALL_BOOKINGS 5 9 0.5556 3.1389 STOP: DENOMINATOR MISMATCH (9 ATTENDED TESTS USED FOR 12 BOOKINGS)
"41.67% of tests taken passed: 5 ÷ 12" ATTENDED_TESTS 5 12 0.4167 3.1389 STOP: DENOMINATOR MISMATCH (12 BOOKINGS INCLUDES 3 NON-ATTENDED)
"50% of attended candidates passed their first recorded test in this export" FIRST_ATTENDED_CANDIDATES 3 6 0.5000 0.0000 RECONCILED: CLAIM MATCHES QUESTION GRAIN AND DENOMINATOR
"62.5% of candidates have a recorded pass" BOOKED_CANDIDATES_ANY_PASS 5 8 0.6250 0.0000 RECONCILED: CLAIM MATCHES QUESTION GRAIN AND DENOMINATOR
41.67% of bookings, with O7 = 8 and O8 = 4 ALL_BOOKINGS 5 12 0.4167 2.0000 STOP: SUPPORTING SUMMARY COUNT MISMATCH VS SOURCE
Google Sheets Bookings!M1:O15 audit block showing the benchmark column N reconciled for BOOKED_CANDIDATES_ANY_PASS at 5 over 8 = 0.625 while column O enters a claimed 5 over 12 = 0.4167 student pass rate, returning a 4.2083 gap in O14 and halting with STOP: ROW GRAIN MISMATCH (12 BOOKINGS USED FOR 8 CANDIDATES) in O15.
Entering the 41.7% student pass-rate claim (5 divided by 12 booking rows under BOOKED_CANDIDATES_ANY_PASS) in column O produces a 4.2083 gap in O14 and halts with STOP: ROW GRAIN MISMATCH (12 BOOKINGS USED FOR 8 CANDIDATES) in O15. Open full-size image.

The first claim divides by bookings but talks about students. The second and third use the right numerator with the wrong denominator for the words in the claim. Two cancellations and a no-show are not tests taken. The two sound claims pass, so the check doesn’t reject a correct number just because it differs from 41.67%.

The last claim is harder to spot. Its 8 attended and 4 non-attended still add up to 12, so it agrees with itself. O15 stops it because 8 and 4 don’t match the 9 and 3 in column N.

O14 compares rates rounded to four decimals, so “matches” means equal at that precision. To see an input mistake, start from the 62.5% claim and type ‘2026-09-01 in O2, with a leading apostrophe. That stores text, which can look like a date. In a spare cell such as Q2, =ISNUMBER(O2) returns FALSE, and O15 says STOP: EXTRACT WINDOW MISMATCH. Then enter =DATE(2026,9,1) in O2. Check that Q2 says TRUE and O15 says RECONCILED again, then clear Q2.

Before you report a rate, column E must say OK on every row, and N15 and O15 must both start with RECONCILED. Treat any STOP message or spreadsheet error, like #VALUE!, as a stop, never a pass. The block checks this layout with clean inputs. It can’t tell you whether the export itself is complete.

These formulas work in Google Sheets and Excel for the web with US English settings.

Google Sheets Bookings!M1:O15 audit block with BOOKED_CANDIDATES_ANY_PASS in O4 and 5 over 8 = 0.625 in O11:O13 matching the source benchmark in column N, returning 0 in O14 and RECONCILED: CLAIM MATCHES QUESTION GRAIN AND DENOMINATOR in O15.
Matching both numerator and denominator to booked candidates (5 candidates with a recorded pass divided by 8 distinct candidates = 0.625) returns 0 in O14 and RECONCILED: CLAIM MATCHES QUESTION GRAIN AND DENOMINATOR in O15. Open full-size image.

Adapting the Sheet to More Bookings

For 20 bookings, in this order:

  1. Enter the new bookings in A14:D21. Don’t insert whole rows, which would shift M:O.
  2. Select E2:O15 and use Find and Replace, searching inside formulas. Replace $13 with $21 (291 places), then <12 with <20 (78 places).
  3. Only then fill E2:K2 down to row 21, and check that column E says OK on every row.

If you fill first, rows 16 to 21 keep checking only rows 2 to 13. For fewer bookings, replace with the smaller numbers, then clear the unused rows in A:K only. Never replace a bare 12 or 13, which would break N12, N13, O12, and O13.

What to Say in the Room

Try: “That 41.7% counts all 12 bookings, including two cancellations, a no-show, and retakes. Of the 9 tests actually sat, 55.6% were passes. Of the 8 candidates with a test scheduled September 1 to 4, 62.5% have a recorded pass in this export.”

Then agree on which question the meeting needs answered, and report that one number with its denominator.

What to Do Next

For order lines, What Does One Row Represent in Your Sales Spreadsheet? covers repeated order totals. For customers, Calculate Repeat-Customer Rate from an Order Export divides by customers, not order rows. To see how your metrics, data, and reports hold up more broadly, try the SMB analytics self-assessment.

This is the first Grain First article, a series on reading what one row represents. Its practice lessons are still being built.

Copy-Paste Formula Reference

E2, the row check. Fill it down to E13:

=IF(OR(COUNTA(A2:D2)<4, NOT(ISTEXT(A2)), NOT(ISTEXT(B2)), NOT(ISNUMBER(C2)), NOT(ISTEXT(D2)), NOT(ISNUMBER($N$2)), NOT(ISNUMBER($N$3)), TRIM(SUBSTITUTE(A2, CHAR(160), " "))="", TRIM(SUBSTITUTE(B2, CHAR(160), " "))="", NOT(EXACT(A2, TRIM(SUBSTITUTE(A2, CHAR(160), " ")))), NOT(EXACT(B2, TRIM(SUBSTITUTE(B2, CHAR(160), " ")))), NOT(EXACT(A2, UPPER(A2))), NOT(EXACT(B2, UPPER(B2))), NOT(ISERROR(FIND(" ", A2&B2))), NOT(ISERROR(FIND("*", A2&B2))), NOT(ISERROR(FIND("?", A2&B2))), NOT(ISERROR(FIND("~", A2&B2))), NOT(ISERROR(FIND("=", A2&B2))), NOT(ISERROR(FIND("<", A2&B2))), NOT(ISERROR(FIND(">", A2&B2))), NOT(OR(EXACT(D2, "pass"), EXACT(D2, "fail"), EXACT(D2, "cancelled"), EXACT(D2, "no_show")))), "CHECK ROW", IF(OR(INT($N$2)<>$N$2, INT($N$3)<>$N$3, $N$2<DATE(2000,1,1), $N$3>DATE(2100,1,1), $N$3<$N$2, INT(C2)<>C2, C2<$N$2, C2>$N$3, SUMPRODUCT(--EXACT($B$2:$B$13, B2), --($C$2:$C$13=C2))>1), "CHECK ROW", "OK"))

O14, the gap between the claim and the booking table:

=IF(OR(N5>0, NOT(ISNUMBER(N6)), NOT(ISNUMBER(N14)), COUNTIF($F$2:$F$13, ">1")>0, N14<>0, NOT(ISNUMBER(O2)), NOT(ISNUMBER(O3)), NOT(ISNUMBER(O5)), NOT(ISNUMBER(N13))), "CHECK INPUTS", IF(OR(INT(O2)<>O2, INT(O3)<>O3, O2<>N2, O3<>N3, INT(O5)<>O5, O5<>0), "CHECK INPUTS", IF(AND(EXACT(O6, "NOT TESTED"), EXACT(O7, "NOT TESTED"), EXACT(O8, "NOT TESTED"), EXACT(O9, "NOT TESTED"), EXACT(O10, "NOT TESTED"), EXACT(O11, "NOT TESTED"), EXACT(O12, "NOT TESTED"), EXACT(O13, "NOT TESTED")), "NOT TESTED", IF(OR(NOT(ISNUMBER(O6)), NOT(ISNUMBER(O7)), NOT(ISNUMBER(O8)), NOT(ISNUMBER(O9)), NOT(ISNUMBER(O10)), NOT(ISNUMBER(O11)), NOT(ISNUMBER(O12)), NOT(ISNUMBER(O13))), "CHECK INPUTS", IF(OR(INT(O6)<>O6, O6<=0, INT(O7)<>O7, O7<0, INT(O8)<>O8, O8<0, INT(O9)<>O9, O9<0, INT(O10)<>O10, O10<0, INT(O11)<>O11, O11<0, INT(O12)<>O12, O12<=0, O13<0), "CHECK INPUTS", ROUND(ABS(O6-N6)+ABS(O7-N7)+ABS(O8-N8)+ABS(O9-N9)+ABS(O10-N10)+ABS(O11-N11)+ABS(O12-N12)+ABS(O13-N13)+ABS(O13-ROUND(O11/O12, 4))+ABS((O7+O8)-O6), 4))))))

O15, the claim’s status:

=IF(OR(N5>0, NOT(ISNUMBER(N6))), "STOP: INVALID OR BLANK SOURCE ROWS", IF(COUNTIF($F$2:$F$13, ">1")>0, "STOP: DUPLICATE BOOKING IDS IN SOURCE", IF(OR(NOT(ISNUMBER(N14)), N14<>0), "STOP: SOURCE EXCLUSION GAP", IF(IF(AND(ISNUMBER(O2), ISNUMBER(O3)), OR(INT(O2)<>O2, INT(O3)<>O3, O2<>N2, O3<>N3), TRUE), "STOP: EXTRACT WINDOW MISMATCH", IF(EXACT(N11, "CHECK SCOPE"), "STOP: UNKNOWN QUESTION SCOPE", IF(IF(ISNUMBER(O5), OR(INT(O5)<>O5, O5<>0), TRUE), "STOP: INVALID CLAIM ROW-CHECK COUNT", IF(EXACT(O14, "NOT TESTED"), "NOT TESTED: ENTER CANDIDATE CLAIM VALUES", IF(EXACT(O14, "CHECK INPUTS"), "STOP: NON-NUMERIC, NEGATIVE, OR NON-INTEGER CLAIM INPUTS", IF(ROUND(ABS((O7+O8)-O6), 4)>0, "STOP: UNRECONCILED EXCLUSIONS (ATTENDED + NON-ATTENDED <> BOOKINGS)", IF(OR(O6<>N6, O7<>N7, O8<>N8, O9<>N9, O10<>N10), "STOP: SUPPORTING SUMMARY COUNT MISMATCH VS SOURCE", IF(AND(EXACT(O4, "BOOKED_CANDIDATES_ANY_PASS"), O12=N6, N6<>N9), "STOP: ROW GRAIN MISMATCH (" & N6 & " BOOKINGS USED FOR " & N9 & " CANDIDATES)", IF(AND(EXACT(O4, "ALL_BOOKINGS"), O12=N7, N7<>N6), "STOP: DENOMINATOR MISMATCH (" & N7 & " ATTENDED TESTS USED FOR " & N6 & " BOOKINGS)", IF(AND(EXACT(O4, "ATTENDED_TESTS"), O12=N6, N6<>N7), "STOP: DENOMINATOR MISMATCH (" & N6 & " BOOKINGS INCLUDES " & N8 & " NON-ATTENDED)", IF(O12<>N12, "STOP: DENOMINATOR COUNT MISMATCH FOR QUESTION SCOPE", IF(O11<>N11, "STOP: NUMERATOR COUNT MISMATCH FOR QUESTION SCOPE", IF(O14=0, "RECONCILED: CLAIM MATCHES QUESTION GRAIN AND DENOMINATOR", "STOP: PASS RATE ROUNDING OR SUMMARY MISMATCH"))))))))))))))))

Continue Reading

Email Notifications

Get notified when new Practical Analytics articles are published.

Field notes on table grain, trustworthy KPIs, and AI-assisted data workflows for small and medium businesses. Sent only when a new guide goes live.

No spam or automated drip funnels. Unsubscribe anytime.