数据分析不仅仅是制作图表;它是将原始、混乱的数据转化为可操作的洞察。虽然初学者止步于 GROUP BY,但专业分析师知道 SQL 的真正力量在于数据清理 、时间序列分析 和流程建模。

专业数据分析师将数据库查询转换为可操作的洞察和可视化
在这份综合指南中,我们将使用一个真实的电商数据集完成一个完整的分析工作流程。我们不仅仅运行查询;我们要解决实际的业务问题。
场景:电商性能评估
想象你是一家在线零售商的首席分析师。你的利益相关者问道:
"我们的用户留存趋势如何?基于最近的消费行为,谁是我们最有价值的客户?"
要回答这个问题,简单的聚合是不够的。我们需要高级技术。
1. 数据清理和准备
现实世界的数据从来都不干净。在分析之前,我们通常需要对数据进行分类或处理缺失值。
使用 CASE 进行数据分箱
连续变量(如价格或年龄)很难汇总。分析师通常使用 CASE 语句将它们"分箱"到类别中。
示例数据(products_binning 表):
| product_id | product_name | price |
|---|---|---|
| 1 | Budget Mouse | 15.00 |
| 2 | Standard Keyboard | 75.00 |
| 3 | Premium Monitor | 350.00 |
| 4 | Budget Headphones | 25.00 |
| 5 | Standard Laptop | 899.00 |
查询示例:
sql
SELECT
product_name,
price,
CASE
WHEN price < 50 THEN 'Budget'
WHEN price BETWEEN 50 AND 200 THEN 'Standard'
ELSE 'Premium'
END as price_category
FROM products_binning;
查询结果:
| product_name | price | price_category |
|---|---|---|
| Budget Mouse | 15.00 | Budget |
| Standard Keyboard | 75.00 | Standard |
| Premium Monitor | 350.00 | Premium |
| Budget Headphones | 25.00 | Budget |
| Standard Laptop | 899.00 | Premium |
为什么使用数据分箱:
- 简化分析:将数千个不同的价格点减少到 3-5 个类别
- 便于可视化:更容易制作柱状图和饼图
- 业务洞察:帮助识别产品定位策略
- 客户细分:根据消费水平对客户分组
常见的分箱场景:
| 变量类型 | 分箱示例 |
|---|---|
| 价格 | Budget (<50), Standard (50-200), Premium (\>200) |
| 年龄 | 青年 (18-25), 中年 (26-45), 老年 (46+) |
| 收入 | 低收入, 中等收入, 高收入 |
| 订单金额 | 小额, 中额, 大额 |
使用 COALESCE 处理 NULL 值
NULL 值会破坏报告。COALESCE 返回第一个非空值,非常适合设置默认值。
示例:
sql
SELECT
customer_name,
COALESCE(phone_number, 'No Phone Provided') as contact_info
FROM customers;
COALESCE 的常见用途:
| 场景 | 示例 |
|---|---|
| 设置默认值 | COALESCE(discount, 0) |
| 合并多个列 | COALESCE(mobile_phone, home_phone, work_phone) |
| 处理可选字段 | COALESCE(middle_name, '') |
| 避免除零错误 | amount / COALESCE(quantity, 1) |
2. 时间序列分析
业务随时间发生。分析趋势(环比、同比)可能是分析师最常见的任务。
日期截断和趋势分析
我们通常按月或周聚合数据,而不是查看每日数据。
示例数据(orders_time 表):
| order_id | order_date | amount |
|---|---|---|
| 1 | 2023-01-15 | 250.00 |
| 2 | 2023-01-20 | 180.00 |
| 3 | 2023-02-05 | 320.00 |
| 4 | 2023-02-18 | 450.00 |
| 5 | 2023-03-10 | 280.00 |
查询示例:
sql
SELECT
strftime('%Y-%m', order_date) as sales_month,
SUM(amount) as total_revenue
FROM orders_time
GROUP BY 1
ORDER BY 1;
查询结果:
| sales_month | total_revenue |
|---|---|
| 2023-01 | 430.00 |
| 2023-02 | 770.00 |
| 2023-03 | 280.00 |
不同数据库的日期函数:
| 数据库 | 按月聚合 | 按周聚合 |
|---|---|---|
| SQLite | strftime('%Y-%m', date_col) |
strftime('%Y-W%W', date_col) |
| PostgreSQL | DATE_TRUNC('month', date_col) |
DATE_TRUNC('week', date_col) |
| MySQL | DATE_FORMAT(date_col, '%Y-%m') |
YEARWEEK(date_col) |
| SQL Server | FORMAT(date_col, 'yyyy-MM') |
DATEPART(week, date_col) |
时间序列分析的关键指标:
- 趋势:数据是上升、下降还是平稳?
- 季节性:是否有周期性模式?
- 异常值:哪些数据点不寻常?
- 增长率:变化的速度有多快?
3. 使用窗口函数的高级分析
这是你从"SQL 用户"升级到"数据分析师"的地方。窗口函数允许你在与当前行相关的一组表行上执行计算。

SQL 窗口函数:展示分区、排名和累计总计用于高级数据分析
累计总计
我们年初至今产生了多少累计收入?
示例数据(daily_sales_window 表):
| sales_date | daily_revenue |
|---|---|
| 2023-01-01 | 1000.00 |
| 2023-01-02 | 1500.00 |
| 2023-01-03 | 1200.00 |
| 2023-01-04 | 1800.00 |
查询示例:
sql
SELECT
sales_date,
daily_revenue,
SUM(daily_revenue) OVER (ORDER BY sales_date) as cumulative_revenue
FROM daily_sales_window
ORDER BY sales_date;
查询结果:
| sales_date | daily_revenue | cumulative_revenue |
|---|---|---|
| 2023-01-01 | 1000.00 | 1000.00 |
| 2023-01-02 | 1500.00 | 2500.00 |
| 2023-01-03 | 1200.00 | 3700.00 |
| 2023-01-04 | 1800.00 | 5500.00 |
窗口函数的关键概念:
- OVER 子句:定义窗口的范围
- ORDER BY:定义行的顺序
- PARTITION BY:将数据分组
- 窗口帧:ROWS 或 RANGE
常见的窗口函数应用:
| 函数 | 用途 | 示例 |
|---|---|---|
SUM() OVER |
累计总计 | 年初至今收入 |
AVG() OVER |
移动平均 | 7 天移动平均 |
ROW_NUMBER() |
行号 | 分页 |
RANK() |
排名(有间隙) | 排行榜 |
DENSE_RANK() |
排名(无间隙) | 成绩排名 |
LAG() |
访问前一行 | 环比增长 |
LEAD() |
访问后一行 | 预测 |
排名和 Top N 分析
每个地区的前 2 名客户是谁?简单的 LIMIT 在这里不起作用,因为我们想要每个组的前 N 名。使用 RANK() 或 ROW_NUMBER()。
示例数据(customers_ranking 表):
| customer_id | customer_name | region | spent |
|---|---|---|---|
| 1 | Alice | North | 5000.00 |
| 2 | Bob | North | 3000.00 |
| 3 | Charlie | North | 2000.00 |
| 4 | Diana | South | 4500.00 |
| 5 | Eve | South | 3500.00 |
| 6 | Frank | South | 2500.00 |
查询示例:
sql
SELECT * FROM (
SELECT
customer_name,
region,
spent,
RANK() OVER (PARTITION BY region ORDER BY spent DESC) as rank
FROM customers_ranking
)
WHERE rank <= 2;
查询结果:
| customer_name | region | spent | rank |
|---|---|---|---|
| Alice | North | 5000.00 | 1 |
| Bob | North | 3000.00 | 2 |
| Diana | South | 4500.00 | 1 |
| Eve | South | 3500.00 | 2 |
RANK vs DENSE_RANK vs ROW_NUMBER:
| 函数 | 并列处理 | 示例(分数:100, 100, 90) |
|---|---|---|
ROW_NUMBER() |
不处理并列 | 1, 2, 3 |
RANK() |
跳过排名 | 1, 1, 3 |
DENSE_RANK() |
不跳过排名 | 1, 1, 2 |
何时使用哪个函数:
- ROW_NUMBER():需要唯一行号(分页)
- RANK():需要跳过并列后的排名(奥运会排名)
- DENSE_RANK():需要连续排名(成绩排名)
4. 使用 CTE 的复杂逻辑
当查询变长时,它们变得难以阅读。CTE(WITH 子句)让你将复杂逻辑分解为可读的步骤。
假设我们想找到"高价值流失客户"------消费超过 $500 但在过去 6 个月内没有购买任何东西的客户。
查询示例:
sql
WITH CustomerStats AS (
SELECT
customer_id,
SUM(amount) as total_spend,
MAX(order_date) as last_order_date
FROM orders
GROUP BY customer_id
),
ChurnedCustomers AS (
SELECT *
FROM CustomerStats
WHERE last_order_date < DATE('now', '-6 months')
)
SELECT *
FROM ChurnedCustomers
WHERE total_spend > 500;
CTE 的优势:
- 可读性:将复杂查询分解为逻辑步骤
- 可维护性:更容易调试和修改
- 可重用性:可以在同一查询中多次引用 CTE
- 协作:团队成员更容易理解
CTE 的常见用途:
| 场景 | 描述 |
|---|---|
| 多步骤计算 | 先计算中间结果,再进行最终计算 |
| 递归查询 | 处理层次结构数据(组织架构、评论树) |
| 数据清理 | 先清理数据,再进行分析 |
| 复杂过滤 | 先应用多个过滤条件,再聚合 |
CTE vs 子查询:
| 特性 | CTE | 子查询 |
|---|---|---|
| 可读性 | ✅ 高 | ⚠️ 中等 |
| 可重用性 | ✅ 可以多次引用 | ❌ 不能重用 |
| 递归 | ✅ 支持 | ❌ 不支持 |
| 性能 | ⚠️ 相似 | ⚠️ 相似 |
5. 环比增长 (MoM)
最后,准确的增长指标通常需要将当前性能与过去性能进行比较。LAG() 允许你访问前一行的数据,而无需自连接。
示例数据(sales_mom 表):
| sale_id | sales_date | amount |
|---|---|---|
| 1 | 2023-01-15 | 10000.00 |
| 2 | 2023-01-20 | 12000.00 |
| 3 | 2023-02-05 | 15000.00 |
| 4 | 2023-02-18 | 14000.00 |
| 5 | 2023-03-10 | 18000.00 |
查询示例:
sql
WITH MonthlySales AS (
SELECT
strftime('%Y-%m', sales_date) as month,
SUM(amount) as revenue
FROM sales_mom
GROUP BY 1
)
SELECT
month,
revenue,
LAG(revenue) OVER (ORDER BY month) as prev_month_revenue,
ROUND(100.0 * (revenue - LAG(revenue) OVER (ORDER BY month)) /
LAG(revenue) OVER (ORDER BY month), 2) as growth_pct
FROM MonthlySales
ORDER BY month;
查询结果:
| month | revenue | prev_month_revenue | growth_pct |
|---|---|---|---|
| 2023-01 | 22000.00 | NULL | NULL |
| 2023-02 | 29000.00 | 22000.00 | 31.82 |
| 2023-03 | 18000.00 | 29000.00 | -37.93 |
LAG 和 LEAD 的用途:
| 函数 | 方向 | 常见用途 |
|---|---|---|
LAG() |
向前看(前一行) | 环比增长、变化检测 |
LEAD() |
向后看(后一行) | 预测、趋势分析 |
增长指标计算:
| 指标 | 公式 | SQL 示例 |
|---|---|---|
| 环比增长 (MoM) | (本月 - 上月) / 上月 | (revenue - LAG(revenue)) / LAG(revenue) |
| 同比增长 (YoY) | (今年 - 去年) / 去年 | (revenue - LAG(revenue, 12)) / LAG(revenue, 12) |
| 复合增长率 (CAGR) | ((结束值 / 开始值)^(1/年数)) - 1 | 需要多步计算 |
最佳实践:
- 使用
COALESCE处理第一行的 NULL 值 - 使用
ROUND格式化百分比 - 考虑使用
NULLIF避免除零错误 - 添加注释说明计算逻辑
总结
SQL 中的数据分析远不止检索行。通过掌握这些模式,你可以直接在数据库中回答复杂的业务问题:
5 大核心技术
-
分箱和清理:对数据进行分类以便更好地汇总
- 使用 CASE 进行数据分箱
- 使用 COALESCE 处理 NULL 值
-
日期计算:理解随时间变化的趋势
- 日期截断和聚合
- 不同数据库的日期函数
-
窗口函数:计算累计聚合和排名
- 累计总计
- 排名和 Top N 分析
- PARTITION BY 分组计算
-
CTE:组织复杂逻辑
- 将查询分解为可读步骤
- 提高可维护性和协作效率
-
Lag/Lead:分析增长指标
- 环比增长计算
- 同比增长分析
从数据录入到数据分析
这些工具将数据录入 与数据分析区分开来:
| 数据录入 | 数据分析 |
|---|---|
| SELECT * FROM table | 使用窗口函数计算累计总计 |
| GROUP BY 简单聚合 | 使用 PARTITION BY 分组排名 |
| 手动计算增长率 | 使用 LAG() 自动计算 |
| 复制粘贴到 Excel | 直接在 SQL 中完成分析 |
实际应用场景
这些技术可以应用于:
电商分析:
- 客户细分(RFM 分析)
- 产品性能分析
- 购物车放弃率分析
SaaS 产品:
- 用户留存分析
- 功能使用率分析
- 订阅流失预测
金融分析:
- 交易趋势分析
- 风险评估
- 欺诈检测
营销分析:
- 渠道效果分析
- 转化漏斗分析
- A/B 测试结果分析
掌握这些 SQL 数据分析技术,你将能够直接在数据库中回答复杂的业务问题,无需依赖外部工具!
相关文章推荐
- Calculating Percentiles and Median in SQL - AVG 告诉你平均值,但中位数和百分位数呢?
- Ranking Data with SQL: RANK, DENSE_RANK, and ROW_NUMBER Explained - 构建排行榜、查找顶级表现者或分页结果
- Mastering SQL LEAD and LAG Functions for Row Comparisons - 需要将一行与前一行或后一行进行比较?学习 LEAD 和 LAG
本文转载自 www.hisqlboy.com
原文标题:SQL for Data Analysis: The Ultimate Guide
原文链接:https://www.hisqlboy.com/blog/sql-for-data-analysis
原作者:SQL Boy Team
转载日期:2026-02-12