In a fundraising report, zero means the team measured an eligible opportunity and confirmed that nothing happened. Null means the value is unknown, missing, not yet observed, or not applicable. Those states should not be displayed or calculated as if they were interchangeable.
The practical rule is simple: use zero only when the reporting contract proves an event could have happened, the observation window is complete enough, and the source data is present. Otherwise, preserve the unknown state and label why it exists.
That distinction changes totals, averages, rates, trends, and the decisions made from them. A dashboard that turns every blank into zero may look complete while quietly converting data problems into performance problems.
What is the difference between null and zero?
Zero is a measured value. It says the relevant count or amount is none. Null is the absence of a usable value. It does not say whether the true value is zero, positive, negative, or inapplicable.
Fundraising reports often need two additional states:
- Not applicable: the metric does not belong to that record, segment, or period.
- Pending: the value is expected but the observation or processing window is still open.
A blank cell cannot communicate all four meanings. If the interface must display a blank, dash, or symbol, its underlying state still needs to remain explicit in the data model and metric definition.
Use a four-state contract for fundraising reports
| State | Meaning | Fundraising example | Calculation treatment |
|---|---|---|---|
| Confirmed zero | The record was eligible, the window was observed, and no qualifying event occurred. | A delivered appeal produced no gifts through the closed attribution window. | Include as 0. |
| Unknown or missing | The value cannot be determined from the available data. | Gift-source data did not load for one campaign. | Exclude from the metric denominator or hold the metric, then report coverage. |
| Not applicable | The metric does not apply to this record. | Event attendance for a direct-mail-only appeal. | Exclude from numerator and denominator. |
| Pending | The value may become known after more time or processing. | Matching-gift revenue inside an open verification window. | Keep separate; do not finalize the metric yet. |
This contract belongs in the reporting rules registry beside the metric grain, eligibility rules, source, observation window, and owner. The display label is the last step, not the definition.
Why replacing null with zero changes the story
Consider an illustrative channel report for a month-end campaign review.
| Channel | Eligible contacts | Confirmed gifts | Correct state | Gift rate |
|---|---|---|---|---|
| 10,000 | 200 | Measured | 2.0% | |
| Paid social | 5,000 | 0 | Confirmed zero | 0.0% |
| Direct mail | 8,000 | Unknown | Gift file missing | Not reportable |
| Matching gift | 300 | Pending | Window open | Not final |
Email and paid social can be combined for a measured gift rate: 200 gifts divided by 15,000 eligible contacts, or 1.33%. Direct mail should not be added as zero because its gift result is unknown. Doing so would produce 200 divided by 23,000, or 0.87%, falsely turning a missing file into weak response. The matching-gift row should remain pending until its measurement window closes.
The report should show both the measured result and its coverage:
- Measured gift rate: 1.33% across eligible contacts with complete results.
- Contact coverage: 15,000 of 23,000 currently eligible contacts, or 65.2%.
- Release status: hold for an all-channel comparison because direct-mail results are missing and could change the decision.
The figures are illustrative. The lesson is structural: a rate without coverage can appear precise even when a material share of the intended population is unresolved.
Nulls affect SQL totals, averages, and counts differently
SQL reporting logic makes the distinction operational. PostgreSQL documents that count(*) counts input rows, while count(expression) counts rows where that expression is not null. It also documents that most aggregate functions ignore null inputs and that sum returns null, not zero, when no rows are selected.
That means these expressions answer different questions:
COUNT(*) -- How many rows are present?
COUNT(gift_amount) -- How many rows have a known gift amount?
SUM(gift_amount) -- What is the total of known gift amounts?
COALESCE(SUM(gift_amount), 0) -- Show zero when the business rule permits it
Source: PostgreSQL 18 aggregate function documentation, reviewed October 21, 2026.
COALESCE is not inherently wrong. It is a function that substitutes the first non-null value. The risk is applying it before proving what the null means. Use it after eligibility, source completeness, and window status have established that “no rows” truly means “no qualifying activity.”
Ask three questions before turning null into zero
- Was the record eligible? A donor who never received the appeal cannot have a confirmed zero response to that appeal.
- Was the full observation window available? A donor at day 10 does not have a confirmed zero 30-day conversion result.
- Did every required source arrive and pass validation? A missing CRM import or processor file creates an unknown result, not a zero result.
Only when all three answers are yes should a missing event commonly become a confirmed zero. If eligibility is false, use not applicable. If time remains, use pending. If a required source is absent or unusable, use unknown and quantify the coverage gap.
Preserve missingness through the reporting workflow
Store the value and the reason separately
A durable reporting model keeps the metric value separate from a status or reason field. For example:
gift_count = nullgift_count_status = 'missing_source'expected_source = 'crm_gift_export'as_of = '2026-10-21T08:00:00-04:00'
The same null value could otherwise mean not eligible, source missing, window open, suppressed for privacy, failed validation, or not yet calculated. A reason field makes the state usable for quality checks and action queues.
Show coverage beside the metric
For any decision-driving metric with unresolved values, show a coverage measure such as known records divided by expected eligible records. Coverage does not repair the metric, but it tells readers how much of the intended population actually supports it.
A fundraising data freshness report can reveal whether the missingness comes from timing. The fundraising exception report provides a pattern for routing unresolved values to an owner instead of burying them in a footnote.
Do not let the visualization invent meaning
Some charting and dashboard layers display nulls as blanks, gaps, zeros, or omitted categories. Test the final visual, not only the query. A line dropping to zero suggests measured collapse; a gap suggests no reportable value. Neither is sufficient without a clear status label when the distinction affects a decision.
Use a decision-specific release rule
Not every null requires the report to stop. The release decision depends on whether unresolved values could change the action.
- Ready: all material values are measured, confirmed zero, or validly not applicable; pending and unknown values are below an agreed threshold and cannot change the decision.
- Qualified: unresolved values remain, but their scope and likely effect are bounded and disclosed.
- Hold: missing or pending values could reverse the comparison, change the ranking, or materially alter the total or rate.
Apply the decision-reversal test: calculate the plausible result if unresolved values are low, typical, and high. If the recommended action changes across those scenarios, the report is not ready for that action.
This rule fits the broader fundraising report preflight: verify source freshness, completeness, definitions, denominators, and comparison parity before the numbers travel to leadership.
A seven-check null-versus-zero audit
- Define the report grain and eligible population.
- Set the observation window and as-of time.
- List every required source and its expected arrival.
- Assign each unresolved value a reason: unknown, not applicable, or pending.
- Confirm which business rules may convert missing events to zero.
- Recalculate totals, averages, counts, and rates with explicit denominators.
- Show coverage and apply the decision-reversal test before release.
Start with one decision-driving metric this week. Count its confirmed zeros, unknowns, not-applicable records, and pending values separately. Then inspect every place where the query, spreadsheet, or visualization replaces blanks with zero. That small audit can uncover whether a weak-looking result is real performance, an open window, or simply missing evidence.
ReportWerks helps nonprofit teams connect fundraising, marketing, and performance data into reporting views where definitions, source status, and decision context remain visible. The goal is not to eliminate nulls. It is to stop unknowns from masquerading as answers.




