What is sales cycle length, and how do you measure it in Metabase?
Sales cycle length is how long a deal takes to close — typically from creation (or first activity) to won. It's the core velocity metric for a sales team, and it's heavily right-skewed, so how you summarize it matters as much as how you measure it. Track it in Metabase from CRM data synced into a database (HubSpot, Salesforce, Pipedrive, and others).
How is sales cycle length defined?
The elapsed days between a deal's start and its win. Teams vary on the start point:
- Created → won — simplest and most common; uses the deal's creation timestamp.
- First activity → won — starts the clock at genuine engagement, ignoring dormant records.
- Qualified → won — starts when a deal enters a qualified stage; needs stage history.
Pick one start definition and apply it consistently.
Why sales cycle length needs stage history
A current CRM snapshot tells you where a deal is now, not when it entered or left each stage. Without stage-change history you can compute overall cycle length (created → won) but nottime-in-stage or where deals stall. Sync the stage/deal-change log:
- HubSpot: deal property history (stage change timestamps).
- Salesforce:
OpportunityFieldHistoryorOpportunityHistoryfor stage transitions. - Pipedrive: deal flow / changes log.
- Close: opportunity status changes (webhooks or activity log).
What data does sales cycle length need?
- A
created_at(or first-activity) timestamp for the start. - A
closed_at/ won timestamp and a clear "won" definition. - For time-in-stage: full stage-change history per deal.
- Segmentation columns: pipeline, segment, deal size band, owner.
SQL patterns
SELECT
date_trunc('month', closed_at) AS month,
percentile_cont(0.5) WITHIN GROUP (
ORDER BY EXTRACT(EPOCH FROM (closed_at - created_at)) / 86400.0
) AS median_cycle_days,
percentile_cont(0.9) WITHIN GROUP (
ORDER BY EXTRACT(EPOCH FROM (closed_at - created_at)) / 86400.0
) AS p90_cycle_days
FROM modeled_deals
WHERE is_won
GROUP BY 1
ORDER BY 1;-- Time-in-stage from stage-change history
WITH ordered AS (
SELECT
deal_id,
stage,
changed_at,
LEAD(changed_at) OVER (
PARTITION BY deal_id ORDER BY changed_at
) AS next_changed_at
FROM modeled_stage_history
)
SELECT
stage,
percentile_cont(0.5) WITHIN GROUP (
ORDER BY EXTRACT(EPOCH FROM (COALESCE(next_changed_at, now()) - changed_at)) / 86400.0
) AS median_days_in_stage
FROM ordered
GROUP BY stage
ORDER BY median_days_in_stage DESC;Pitfalls
Where this metric applies
- HubSpot + Metabase — deal create date and property history
- Salesforce + Metabase —
CreatedDateandOpportunityFieldHistory - Pipedrive + Metabase —
add_timeand deal flow - Close + Metabase —
date_createdand status changes