Row Exists in the Detail Query but Not the Summary? Cross-Query Predicate Granularity Mismatch
While triaging a dashboard lead in an ad-data pipeline: an offer was flagged "suspected paused" in the diagnostic detail, complete with a reinvestment suggestion β yet the "suggested actions" list had no row for it. The detail page pointed at a table that didn't contain it.
Encountered this while building AI Analytics β an LLM-powered analytics platform that surfaces market trends, user behavior, and sales data; the diagnostic detail and the suggestion list come from two adjacent queries in the pipeline.
TL;DRβ
Two queries defined the same business predicate ("suspected paused") at different aggregation granularity: the detail query judged at campaignΓoffer level β any single campaign with no spend in the last week flags it; the candidate list judged at whole-offer level β all campaigns must be spend-free to qualify. One offer still had spend (3.09) in one campaign, so the detail flagged it while the summary skipped it, leaving a dangling reference in the UI. Three fixes: unify the definition at the coarser granularity, make the predicate CTEs verbatim-identical, and verify row-set containment before cross-referencing.
Symptomsβ
Two queries, each with its own definition of "suspected paused":
-- Query 4, diagnostic detail: pair granularity (campaign Γ offer)
WITH consumption_paused_q4 AS (
SELECT offer_id, campaign_id
FROM spend_weekly
GROUP BY offer_id, campaign_id -- each campaign judged alone
HAVING SUM(spend) FILTER (WHERE is_last_week) = 0
)
-- Query 5, suggestion candidates: offer granularity (summed across campaigns)
WITH suspected_paused_q5 AS (
SELECT offer_id
FROM spend_weekly
GROUP BY offer_id -- the whole offer judged together
HAVING SUM(spend) FILTER (WHERE is_last_week) = 0
)
The problematic offer hung under multiple campaigns, one of which still had spend (3.09) in its last week:
Query 4 (pair level): campaign A last-week spend = 0 β flagged β, suggestion attached
Query 5 (offer level): summed last-week spend = 3.09 β excluded β
UI: detail says "see the suggestion table"; the suggestion table has no such row
No errors, plausible totals β the dangling reference only surfaces when someone follows a specific detail row.
Root Causeβ
The "same source" contract had a coverage gap. The pipeline spec requires the feature-column CTEs of adjacent queries to be verbatim identical β that rule was followed to the letter. But it only covers feature columns; the predicate set (the business judgment before WHERE) was out of scope. "Suspected paused" was implemented twice, at different granularities: pair-level judgment is sensitive to "one campaign has no spend", offer-level judgment to "all campaigns have no spend". For any offer spanning both situations, the two queries must disagree β the overlap zone is mathematically guaranteed.
Cross-query referencing amplified the fork into a dangling reference: the UI treats query 4's rows as details and query 5's table as the entry point, with nobody ever checking "rows(q4) β rows(q5)". Aggregation-level inconsistency is the classic data-warehouse consistency trap β our earlier post on SUM over ratio columns inflating aggregated metrics is the same disease in another organ: aggregation happening at the wrong level.
Solutionβ
Step 1: Decide the canonical definition before touching SQLβ
The business question has one answer: "should we keep funding this offer" is an offer-level decision, so the predicate must tighten to whole-offer granularity β every attached campaign spend-free in the last week AND no operation annotation rows. Bump the rule version so the change is traceable.
Step 2: Share the predicate CTE verbatimβ
Extract the predicate CTE into one text block referenced by both queries; upgrade the contract at the same time: verbatim sharing covers the predicate set (including granularity), not just feature columns. One definition, one place to change.
Step 3: Before cross-referencing, check row-set containmentβ
Make "marker set β target row set" a standing check (promote it to a test):
-- Dangling detection: flagged by q4 but absent from q5
SELECT q4.offer_id
FROM consumption_paused_q4 q4
LEFT JOIN suspected_paused_q5 q5 USING (offer_id)
WHERE q5.offer_id IS NULL;
Only when this returns 0 rows does the UI earn the right to render both outputs on one page.
Step 4: Replay against history before shippingβ
Replay the new definition over historical snapshots: verify every verdict flip (flagged β normal) is correct, with zero collateral flips and zero dangling references, then re-run production validation.
Notes
- "Same source" contracts must cover predicate granularity, not just feature columns; feature-column sharing cannot save you from a predicate defined twice.
- Each business predicate (paused, hot, churning...) gets exactly one definition in the pipeline; a second implementation is an incident in waiting.
- Before adding any cross-query reference (A's output rows pointing at B's output table), run the containment check β don't wait for a user to click a dangling link.
- Version every definition change and replay it over historical data; "it works on new data" hides the risk of existing conclusions silently reversing.
FAQβ
Why do two SQL queries return different results on the same data?β
Three usual differences: aggregation granularity (different GROUP BY dimensions β campaignΓentity vs whole entity in this case), filter logic (different WHERE/HAVING conditions), and point in time. Granularity is the sneakiest: both queries are individually correct, yet their verdicts can contradict.
When do I use HAVING vs WHERE for aggregate conditions in SQL?β
WHERE filters rows before grouping; HAVING filters groups after. But before picking the keyword, pick the granularity β "what counts as one group" determines sensitivity: finer granularity flags more easily (any group qualifies), coarser granularity is conservative (all groups must qualify). Different granularity, opposite verdicts.
How do I keep data definitions consistent across reports?β
One definition per business predicate, predicate CTEs shared verbatim across queries, containment checks (A β B) before cross-query references, and versioned definition changes replayed over history. Consistency doesn't come from convention β it comes from contracts plus checks.
CCLEE
Independent developer, 24 years in e-commerce, focused on grounding AI in real business scenarios.
Work with me