每个企业都有一个"漏斗"------用户从首次访问网站到成为付费客户的路径。但大多数用户会在途中流失。
漏斗分析 帮助你精确定位他们在哪里流失。
- 50% 的用户在着陆页后离开?
- 80% 的用户放弃购物车?
在本指南中,我们将使用 SQL 从头开始构建一个转化漏斗。

转化漏斗阶段及流失百分比
数据模型
我们将使用一个简单的 events_funnel 表来跟踪用户操作:
示例数据(events_funnel 表):
| user_id | event_name | event_time |
|---|---|---|
| 1 | view_landing | 10:00 |
| 1 | add_to_cart | 10:05 |
| 1 | purchase | 10:10 |
| 2 | view_landing | 11:00 |
| 2 | add_to_cart | 11:05 |
| 3 | view_landing | 12:00 |
数据说明:
- user_id:用户唯一标识符
- event_name:事件名称(view_landing、add_to_cart、purchase)
- event_time:事件发生时间
这个简单的事件表可以跟踪用户在转化漏斗中的每个步骤。
方法 1:逐步聚合
构建漏斗最简单的方法是计算执行每个操作的唯一用户数。
查询示例:
sql
-- 简单的漏斗聚合
SELECT
event_name,
COUNT(DISTINCT user_id) as users,
-- 计算占总数的百分比(简化版)
ROUND(100.0 * COUNT(DISTINCT user_id) /
(SELECT COUNT(DISTINCT user_id) FROM events_funnel), 2) as pct
FROM events_funnel
GROUP BY event_name
ORDER BY
CASE event_name
WHEN 'view_landing' THEN 1
WHEN 'add_to_cart' THEN 2
WHEN 'purchase' THEN 3
END;
查询结果:
| event_name | users | pct |
|---|---|---|
| view_landing | 3 | 100.00 |
| add_to_cart | 2 | 66.67 |
| purchase | 1 | 33.33 |
结果解读:
- 着陆页:3 个用户访问(100%)
- 加购物车:2 个用户添加商品(66.67%)
- 购买:1 个用户完成购买(33.33%)
这种方法的问题:
这种方法不保证顺序 。如果一个用户先执行了 purchase,然后才执行 view_landing,他们仍然会被计入两个步骤,即使他们没有遵循漏斗路径。
为什么会出现这个问题?
因为我们只是简单地计算每个事件的唯一用户数,而没有检查事件的顺序。在实际业务中,这可能导致:
- 虚高的转化率:包含了不符合漏斗顺序的用户
- 错误的优化方向:无法准确识别流失点
- 误导性的洞察:数据不反映真实的用户旅程
方法 2:有序漏斗(使用左连接)
为了确保用户遵循步骤 1 -> 步骤 2 -> 步骤 3 的顺序,我们将步骤连接在一起。
示例数据(events_funnel_ordered 表):
| user_id | event_name | event_time |
|---|---|---|
| 1 | view_landing | 10:00 |
| 1 | add_to_cart | 10:05 |
| 1 | purchase | 10:10 |
| 2 | view_landing | 11:00 |
| 2 | add_to_cart | 11:05 |
| 3 | view_landing | 12:00 |
| 4 | purchase | 13:00 |
| 4 | view_landing | 13:05 |
查询示例:
sql
WITH landing_users AS (
SELECT DISTINCT user_id
FROM events_funnel_ordered
WHERE event_name = 'view_landing'
),
cart_users AS (
SELECT DISTINCT user_id
FROM events_funnel_ordered
WHERE event_name = 'add_to_cart'
),
purchase_users AS (
SELECT DISTINCT user_id
FROM events_funnel_ordered
WHERE event_name = 'purchase'
)
SELECT
COUNT(DISTINCT l.user_id) as landing_users,
COUNT(DISTINCT c.user_id) as cart_users,
COUNT(DISTINCT p.user_id) as purchase_users,
ROUND(100.0 * COUNT(DISTINCT c.user_id) / COUNT(DISTINCT l.user_id), 2) as landing_to_cart_pct,
ROUND(100.0 * COUNT(DISTINCT p.user_id) / COUNT(DISTINCT c.user_id), 2) as cart_to_purchase_pct
FROM landing_users l
LEFT JOIN cart_users c ON l.user_id = c.user_id
LEFT JOIN purchase_users p ON c.user_id = p.user_id;
查询结果:
| landing_users | cart_users | purchase_users | landing_to_cart_pct | cart_to_purchase_pct |
|---|---|---|---|---|
| 4 | 2 | 1 | 50.00 | 50.00 |
结果解读:
- 着陆页用户:4 个用户访问着陆页
- 购物车用户:2 个用户添加商品到购物车(从着陆页的转化率为 50%)
- 购买用户:1 个用户完成购买(从购物车的转化率为 50%)
为什么这种方法更好?
使用左连接确保了:
- 顺序保证:只有先访问着陆页的用户才会被计入购物车步骤
- 准确的转化率:反映真实的用户旅程
- 可靠的洞察:帮助识别真正的流失点
查询解析:
- landing_users CTE:提取所有访问着陆页的用户
- cart_users CTE:提取所有添加商品到购物车的用户
- purchase_users CTE:提取所有完成购买的用户
- LEFT JOIN:将步骤连接在一起,确保顺序
- 百分比计算:计算每个步骤之间的转化率
注意事项:
- 用户 4 先购买再访问着陆页,不会被计入有序漏斗
- 这种方法更准确,但查询稍微复杂一些
- 适用于需要严格顺序保证的场景
方法 3:单查询透视
对于高级用户,你可以使用 CASE WHEN 聚合在一次查询中完成。这在大数据集上速度更快。
查询示例:
sql
SELECT
COUNT(DISTINCT user_id) as total_users,
COUNT(DISTINCT CASE WHEN event_name = 'add_to_cart' THEN user_id END) as cart_users,
COUNT(DISTINCT CASE WHEN event_name = 'purchase' THEN user_id END) as purchase_users
FROM events_funnel;
查询结果:
| total_users | cart_users | purchase_users |
|---|---|---|
| 3 | 2 | 1 |
为什么使用这种方法?
性能优势:
- 单次扫描:只需扫描表一次,而不是多次
- 更快的执行:在大数据集上速度显著提升
- 更少的内存:不需要创建多个 CTE
适用场景:
- 数据量大(百万级以上)
- 需要快速响应
- 不需要复杂的顺序检查
局限性:
- 不保证顺序(与方法 1 相同)
- 不适合需要严格顺序保证的场景
- 可读性稍差
性能对比:
| 方法 | 表扫描次数 | 适用数据量 | 顺序保证 |
|---|---|---|---|
| 方法 1:逐步聚合 | 1 次 | 小到中等 | ❌ |
| 方法 2:左连接 | 3 次 | 小到中等 | ✅ |
| 方法 3:单查询透视 | 1 次 | 大 | ❌ |
计算流失率
最重要的洞察是步骤之间的转化率。
示例分析:
如果你有:
- 着陆页:1000 用户
- 购物车:200 用户(20% 转化率)
- 购买:50 用户(25% 转化率)
关键洞察:
你知道最大的问题是让用户将商品添加到购物车,而不是结账!
为什么这很重要?
- 优化方向:应该优先优化着陆页到购物车的转化,而不是购物车到购买的转化
- 投资回报:提升 20% 的转化率比提升 25% 的转化率影响更大(基数更大)
- 资源分配:将资源集中在最大的流失点
流失率计算公式:
| 指标 | 公式 | 示例 |
|---|---|---|
| 转化率 | (下一步用户数 / 当前步用户数) × 100% | (200 / 1000) × 100% = 20% |
| 流失率 | 100% - 转化率 | 100% - 20% = 80% |
| 累计转化率 | (最终步用户数 / 第一步用户数) × 100% | (50 / 1000) × 100% = 5% |
完整的漏斗分析查询:
sql
WITH funnel_steps AS (
SELECT
COUNT(DISTINCT CASE WHEN event_name = 'view_landing' THEN user_id END) as step1_users,
COUNT(DISTINCT CASE WHEN event_name = 'add_to_cart' THEN user_id END) as step2_users,
COUNT(DISTINCT CASE WHEN event_name = 'purchase' THEN user_id END) as step3_users
FROM events_funnel
)
SELECT
step1_users as landing_users,
step2_users as cart_users,
step3_users as purchase_users,
ROUND(100.0 * step2_users / step1_users, 2) as landing_to_cart_conversion,
ROUND(100.0 * (step1_users - step2_users) / step1_users, 2) as landing_to_cart_dropoff,
ROUND(100.0 * step3_users / step2_users, 2) as cart_to_purchase_conversion,
ROUND(100.0 * (step2_users - step3_users) / step2_users, 2) as cart_to_purchase_dropoff,
ROUND(100.0 * step3_users / step1_users, 2) as overall_conversion
FROM funnel_steps;
查询结果示例:
| landing_users | cart_users | purchase_users | landing_to_cart_conversion | landing_to_cart_dropoff | cart_to_purchase_conversion | cart_to_purchase_dropoff | overall_conversion |
|---|---|---|---|---|---|---|---|
| 1000 | 200 | 50 | 20.00 | 80.00 | 25.00 | 75.00 | 5.00 |
如何使用这些数据?
- 识别最大流失点:着陆页到购物车流失 80%
- 优先级排序:先优化着陆页体验和商品展示
- 设定目标:如果将着陆页到购物车的转化率从 20% 提升到 30%,整体转化率将从 5% 提升到 7.5%
- A/B 测试:测试不同的着陆页设计、商品推荐算法等
实战练习:Salesforce 面试题
准备好应用漏斗分析技术了吗?试试这道 Salesforce 面试题:
Lead Conversion Funnel (Salesforce)
计算 B2B 销售漏斗每个阶段的转化率(Lead -> Opportunity -> Closed Won)。这道题挑战你计算计数和不同阶段之间的百分比流失。
问题描述:
给定一个 sales_funnel 表:
| lead_id | stage | stage_date |
|---|---|---|
| 1 | Lead | 2023-01-01 |
| 1 | Opportunity | 2023-01-05 |
| 1 | Closed Won | 2023-01-10 |
| 2 | Lead | 2023-01-02 |
| 2 | Opportunity | 2023-01-06 |
| 3 | Lead | 2023-01-03 |
任务:
计算每个阶段的用户数和转化率:
- Lead 数量
- Opportunity 数量和从 Lead 的转化率
- Closed Won 数量和从 Opportunity 的转化率
- 整体转化率(从 Lead 到 Closed Won)
提示:
- 使用方法 2(左连接)确保顺序
- 计算每个阶段之间的转化率
- 注意处理 NULL 值
扩展挑战:
- 计算每个阶段的平均转化时间
- 按销售代表分组分析
- 识别高转化率和低转化率的特征
结论
使用 SQL 构建漏斗可以让你对数据进行精细控制。与预构建的分析工具不同,你可以:
SQL 漏斗分析的优势
灵活性:
- 定义自定义步骤(不限于预设的漏斗)
- 处理复杂的用户旅程(多路径、循环、回退)
- 按数据库中的任何属性进行过滤(地区、设备、渠道等)
准确性:
- 直接访问原始数据
- 完全控制计算逻辑
- 可以验证和调试结果
可扩展性:
- 适用于任何规模的数据
- 可以优化查询性能
- 易于集成到数据管道
成本效益:
- 无需购买昂贵的分析工具
- 利用现有的数据库基础设施
- 易于维护和更新
学习路径
从简单的聚合开始,随着需求的增长逐步升级到有序漏斗!
初学者:
- 使用方法 1(逐步聚合)快速了解漏斗概念
- 理解转化率和流失率的计算
中级用户:
- 使用方法 2(左连接)构建准确的有序漏斗
- 添加时间维度分析(按周、按月)
- 按用户属性细分(地区、设备、渠道)
高级用户:
- 使用方法 3(单查询透视)优化大数据集性能
- 构建多路径漏斗(允许用户跳过某些步骤)
- 实现漏斗回退分析(用户返回上一步)
- 结合窗口函数进行时间序列分析
实际应用场景
电商平台:
- 访问 -> 浏览 -> 加购 -> 结账 -> 购买
- 识别购物车放弃的原因
- 优化结账流程
SaaS 产品:
- 访问 -> 注册 -> 激活 -> 订阅 -> 续费
- 提升新用户激活率
- 减少订阅流失
内容平台:
- 访问 -> 阅读 -> 点赞 -> 分享 -> 订阅
- 增加用户参与度
- 提升订阅转化率
B2B 销售:
- Lead -> Opportunity -> Proposal -> Negotiation -> Closed Won
- 缩短销售周期
- 提高成交率
移动应用:
- 安装 -> 注册 -> 首次使用 -> 留存 -> 付费
- 优化新用户引导流程
- 提升用户留存和付费转化
最佳实践
数据质量:
- 确保事件数据完整和准确
- 处理重复事件和异常值
- 验证时间戳的正确性
性能优化:
- 为事件表添加索引(user_id, event_name, event_time)
- 使用分区表处理大数据量
- 考虑物化视图缓存常用漏斗
可维护性:
- 使用 CTE 组织复杂查询
- 添加注释说明计算逻辑
- 版本控制漏斗定义
业务洞察:
- 定期监控漏斗变化
- 设置转化率阈值告警
- 结合 A/B 测试验证优化效果
相关文章推荐
- SQL for Data Analysis: The Ultimate Guide - 超越基础 SELECT,掌握真实世界数据分析的核心 SQL 技术
- SQL Window Frames: ROWS vs RANGE - 了解 ROWS 和 RANGE 窗口帧如何改变结果,避免隐藏陷阱
- Calculating Weighted Averages in SQL - 标准平均值可能具有误导性,学习如何计算加权平均值
本文转载自 www.hisqlboy.com
原文标题:Building Conversion Funnels in SQL
原文链接:https://www.hisqlboy.com/blog/building-conversion-funnels-in-sql
原作者:SQL Boy Team
转载日期:2026-02-12