1. SQL是经久不衰的基础
能经过时间考验的SQL,其优点毋庸置疑。
对于日常处理数据的朋友们(BI顾问,数据开发,数仓建模,数据研发,ETL工程师,AI工程师等),SQL更是一项非常重要的基础技能。
这里就不再列举SQL的优点了(很多),而只谈谈SQL使用中的一些问题,这里是系列文章的开篇:复杂SQL不易理解。
2. 讲故事
先讲个故事来示例,注:
- 示例中的表和场景都是经过简化的,实际中可能复杂非常多
- 示例的SQL都不保证是最优的写法
- 示例中的表结构也只是示例作用
数据开发工程师小吴在一家零售企业工作,他最近的工作就是帮助运营小胡分析客户画像。
公司有2张表,都是直接存储在最简单好用的 Postgresql 12.2 数据库中:
- orders:订单表
- customers:客户表
具体内容如下:
orders:
customers
2.1 Step1 - 需要统计每个 customer_id 的总消费额
小吴快速的写了个SQL:
SELECT
customer_id,
SUM(unit * unit_price *
(1
- discount)) AS total_sales
FROM orders
GROUP BY customer_id
ORDER BY total_sales DESC
注:小吴是处女座的,所以SQL还是要经过排版的, 数据也是排好序的。
得到了如下结果:
2.2 Step2 - 加上客户名和过滤掉非正常用户
小胡很快给出了反馈:
- 虽然你是开发,你熟悉于直接用ID称呼客户,但是我不习惯, 我需要看中文名字
- 这个客户ID 2, 我记得很清楚, 是我们的测试用户,上次我们上线后,我就把它从数据库中标记 is_delete 为 True 了,你需要去除掉
小吴说:好的
在解决了如下问题后:
- 查阅了JOIN的几种语法
- 通过表别名解决了错误:column reference "customer_id" is ambiguous
- 通过 max() 解决了错误:column "customers.customer_name" must appear in the GROUP BY clause or be used in an aggregate function
得到了如下SQL (注意:修改散落在多个地方)
SELECT
orders.customer_id,
MAX(customer_name) AS customer_name,
SUM(unit * unit_price *
(1
- discount)) AS total_sales
FROM orders JOIN customers
ON orders.customer_id = customers.customer_id
WHERE customers.is_delete=False
GROUP BY orders.customer_id
ORDER BY total_sales DESC
得到结果:
2.3 Step3 - 复杂的任务来了,要把客户分等级了
运营同学在阿里进修了一门《人人都可以当运营》课程,回来对数据小吴说:小吴呀,我们的会员体系要做起来呀,会员是我们以后上市的支柱,即使对我们的天使轮也是非常有用的呀。而且我学到了:“一定要结合客户所在地做会员分级”,所以,我决定:
- 对于所在地在”上海“的客户:如果他/她的消费额 >= 300, 那么他/她是白金会员,如果在区间 [100, 300), 则是黄金会员,否则就是普通会员
- 对于所在地为”杭州“的客户:如果他/她的消费额 >= 250, 那么他/她是白金会员,如果在区间 [80, 250), 则是黄金会员,否则就是普通会员
小吴这下要好好考虑这个问题了。
2.3.1 同一层SQL上改
首先,他试着在上步骤的SQL中,直接把会员等级这个直接算出来,
SELECT
orders.customer_id,
MAX(customer_name) AS customer_name,
SUM(unit * unit_price *
(1
- discount)) AS total_sales,
CASE city
WHEN '上海' THEN
CASE WHEN SUM(unit * unit_price *
(1
- discount))