What this means for your store
Grain is simply: what does one row represent? Sum Amount on a dataset where each row is one opportunity and the pipeline chart is trustworthy. Join OpportunityLineItem without aggregating first and every SKU line repeats the parent deal value - the usual break when wholesale teams mix quote lines with header totals on the same lens.
Scenario on a real storefront
A UK office-furniture wholesaler wants stage charts at deal level and a separate product attach view at SKU level. Two datasets, two grains - not one bloated join:
# SAQL - roll up to opportunity grain before the dashboard
q = load "opp_line_items_raw";
q = group q by 'OpportunityId';
q = foreach q generate
'OpportunityId' as 'OppId',
sum('TotalPrice') as 'LineRevenue',
max('Amount') as 'OppAmount', -- same on every line; max, not sum
max('StageName') as 'Stage',
max('CloseDate') as 'CloseDate';
q = filter q by 'Stage' != "Closed Lost";
# Lens: bar on Stage, measure sum(OppAmount)
# Trap: sum(LineRevenue) != OppAmount when header discount lives on Opportunity only
What to do next
- Pin a one-line note on the app home: “Pipeline lens =
opp_grain; SKU mix =opp_line_grain.” - When ecommerce orders land as Opportunity + Order, pick one revenue column - summing both in the same widget inflates closed-won.
- Before org-wide rollout, filter SAQL to one known deal: row count for that
OpportunityIdmust be exactly 1.
Bottom line
Wrong grain silently doubles pipeline. Aggregate to opportunity level for stage and forecast lenses; spin up a line-item dataset when reps need SKU detail.