第三章:适应
闲中不放过,忙处有受用。 —— 《菜根谭》
2014年10月中旬,公司开始紧张起来。
所有人都在说一个词:双十一。
那是我入职的第四个月。
我已经学会了透视表,学会了写周报,学会了在沙县小吃点一份八块钱的炒米粉。我以为自己已经适应了这份工作。
直到双十一来了。
九月的最后一周,老板张涛把所有人叫到会议室。
"今年双十一,公司要冲一把。"
他在白板上写了一个数字:500万。
"去年我们做了200万。今年目标翻一倍多。"
我看了看周围的人。大家的表情都很平静,好像这个数字跟他们没什么关系。
张涛继续说:"从下周开始,所有部门进入大促状态。运营那边会有很多临时需求,技术和数据要全力配合。"
他看了一眼老李。
"老李,你们数据这边,负责数据支持。有什么问题随时找我。"
老李点点头,我也跟着点头。
心里有点慌。
什么叫"大促状态"?
我很快就知道了。
第一天,运营提了三个数据需求。
第二天,五个。
第三天,八个。
到了第二周,我的需求列表上已经堆了二十多个任务。每一个都标着"紧急"。
我问老李:"这些都是紧急的吗?"
老李看了一眼:"大促期间,所有需求都是紧急的。"
"那怎么排优先级?"
"谁喊得最凶,谁就排前面。"
他说这话的时候,没有任何表情。我不知道他是在开玩笑还是认真的。
后来我发现,他是认真的。
那段时间,我每天的工作就是跑数据。
运营说:我要看过去30天的销售趋势。
我打开Excel,写公式,拉透视表,半小时搞定。
运营说:我要看各个品类的转化率对比。
我又打开Excel,导数据,做图表,一个小时搞定。
运营说:我要看用户的复购周期分布。
我愣住了。
十万行数据,Excel转了三分钟,然后卡死了。
我只能重启。
那天晚上,我坐在工位上发呆。
老李走过来,看了看我的屏幕。
"又卡了?"
"嗯。数据太多,Excel跑不动。"
老李想了想:"你会SQL吗?"
"一点点。SELECT和WHERE。"
"那就用SQL跑吧。我们有个简单的数据库,能处理这种量级的数据。"
他帮我连上了公司的MySQL数据库。
"你先试试。有问题问我。"
那是我第一次用SQL处理真正的业务数据。
之前在学校学过,但都是考试题——SELECT * FROM students WHERE score > 60。那种数据量只有几十条。
现在面对的是真实的订单表,几十万行。
我写了第一条SQL:
SELECT * FROM orders;
回车。
屏幕刷了两秒,吐出来几万行数据。
我愣住了。
这东西比Excel快多了。
接下来的几天,我开始疯狂学SQL。
白天跑需求,晚上看教程。萌弹、CSDN、菜鸟教程,能找到的资料我都看了一遍。
我学会了GROUP BY——把数据按维度分组统计。
我学会了ORDER BY——把结果排序。
我学会了COUNT、SUM、AVG——各种聚合函数。
我学会了HAVING——对分组后的结果再筛选。
每学会一个新语法,我就拿业务数据试一下。
运营要的复购周期分布?用GROUP BY按用户分组,算每个用户的订单间隔天数,再统计分布。
运营要的各渠道ROI?用GROUP BY按渠道分组,算每个渠道的花费和收入,再相除。
运营要的用户分层?用CASE WHEN按消费金额分档,再COUNT每档的人数。
Excel做不动的事情,SQL几秒钟就跑完了。
我感觉自己像是突然获得了一种超能力。
十月中旬,距离双十一还有不到一个月。
公司的气氛越来越紧张。
运营部门的人开始加班到十点。技术部那两个开发开始加班到十二点——他们要改系统、做活动页面。我和老李也开始加班,但没那么夸张——一般八九点就走了。
"数据的活,能提前做的都提前做。"老李说,"别等着他们来催。"
我照他说的做。
每天下班前,我会把明天可能要的数据提前跑出来。第二天运营一来要,我直接发。他们觉得我响应特别快,其实只是我前一天晚上加了十分钟班。
这是老李教我的第二课:主动比被动强。
然后,出事了。
十月二十号,周一。
早上我刚到公司,就看到老板张涛站在运营部门的工位前,脸色很难看。
运营负责人王姐在跟他解释什么,声音很小,我听不清楚。
老李走过来,压低声音说:"数据出问题了。"
"什么问题?"
"昨晚凌晨更新数据,有个脚本跑错了。导出的用户画像数据是错的。"
"错了多少?"
"全错了。"
我倒吸一口冷气。
用户画像是大促投放的核心数据。每一分钱的广告费都是按画像来投的。如果画像错了,钱就白花了。
"现在怎么办?"
"得重新跑一遍。但那个脚本是技术部一个离职的人写的,没人看得懂。"
老李的表情很平静,但我能感觉到他的焦虑。
"能不能用SQL重写一遍?"
老李想了想:"理论上可以,但逻辑很复杂。要把用户的消费记录、浏览记录、收藏记录全部关联起来,还要做分层。"
"需要多久?"
"如果从头写,至少两天。"
"大促还有二十天。"
"问题是,投放今天就要用。"
我不知道自己为什么会举手。
可能是因为太年轻,不知道这事有多难。
也可能是因为之前几周一直在疯狂学SQL,正好想试试。
"老李,我能看看那个逻辑吗?"
老李看了我一眼:"你有把握?"
"没有。但我想试试。"
他犹豫了一下,然后把那份错误的脚本发给了我。
那是一份存储过程,用SQL Server写的,三百多行。
说实话,我看不太懂。
但我看懂了注释。注释写得很详细,记录了每一步的逻辑:
- 从订单表取用户的消费金额
- 从浏览表取用户的浏览次数
- 从收藏表取用户的收藏商品数
- 把三张表关联起来
- 根据阈值把用户分成ABC三档
逻辑不复杂。复杂的是实现。
我决定不去理解那份存储过程,直接用我会的语法重写。
我打开一个新的查询窗口,开始写。
第一步,取消费金额:
SELECT user_id, SUM(amount) as total_amount
FROM orders
WHERE order_time >= '2014-01-01'
GROUP BY user_id
跑一下,三秒出结果。没问题。
第二步,取浏览次数:
SELECT user_id, COUNT(*) as view_count
FROM page_views
WHERE view_time >= '2014-01-01'
GROUP BY user_id
跑一下,五秒出结果。没问题。
第三步,取收藏数:
SELECT user_id, COUNT(*) as fav_count
FROM favorites
WHERE create_time >= '2014-01-01'
GROUP BY user_id
跑一下,两秒出结果。没问题。
现在问题来了:怎么把三个结果合在一起?
我盯着屏幕想了五分钟。
然后我想起来一个东西:子查询。
子查询就是把一个查询的结果当成另一个查询的输入。
我试着写:
SELECT
a.user_id,
a.total_amount,
b.view_count,
c.fav_count
FROM
(SELECT user_id, SUM(amount) as total_amount FROM orders WHERE order_time >= '2014-01-01' GROUP BY user_id) a
LEFT JOIN
(SELECT user_id, COUNT(*) as view_count FROM page_views WHERE view_time >= '2014-01-01' GROUP BY user_id) b
ON a.user_id = b.user_id
LEFT JOIN
(SELECT user_id, COUNT(*) as fav_count FROM favorites WHERE create_time >= '2014-01-01' GROUP BY user_id) c
ON a.user_id = c.user_id
跑一下。
十五秒。
结果出来了。
我盯着屏幕上的数据,心跳加速。
最后一步,分层。
根据注释里的阈值:
- A类用户:消费大于1000,浏览大于50
- B类用户:消费大于500,或浏览大于30
- C类用户:其他
用CASE WHEN:
SELECT
user_id,
total_amount,
view_count,
fav_count,
CASE
WHEN total_amount > 1000 AND view_count > 50 THEN 'A'
WHEN total_amount > 500 OR view_count > 30 THEN 'B'
ELSE 'C'
END as user_level
FROM (
-- 前面那一大段子查询
) t
跑一下。
二十秒。
结果出来了。
我对比了一下正确的历史数据,逻辑是对的。
整个过程用了两个小时。
老李看到结果的时候,表情有点复杂。
"你这SQL写得有点丑。"他说。
"但是能跑。"
"能跑就行。"
他把数据发给了运营。
运营拿去做了投放。
晚上,张涛走过来,问老李:"数据的问题解决了?"
老李说:"解决了。林默写的SQL。"
张涛看了我一眼。
那是他第一次直接看我。
"不错。"
然后他走了。
就两个字。
但我记了很久。
后来老李告诉我,那天如果数据拿不出来,投放的预算——二十万——就白烧了。
"你知道二十万是什么概念吗?"他问我。
我算了一下。我一个月工资三千。
"相当于我......五年多的工资?"
"对。你今天一个人,救了五年的工资。"
我不知道该说什么。
"但你的SQL确实写得很丑。"老李又说了一遍。
"我知道。"
"回去学学怎么写得好看一点。代码是给人看的,不是给机器看的。"
"好。"
双十一那天,公司全员加班。
我盯着后台的数据看了一整天。订单数一直在涨,像一条疯狂的曲线。
晚上十一点,张涛站在白板前,宣布了最终成绩:
六百三十万。
超额完成。
所有人都在欢呼。有人开了香槟,有人在拍照。王姐哭了,说自己从来没这么累过。
我站在角落里,喝着矿泉水。
老李走过来,拍了拍我的肩膀。
"第一次经历大促,什么感觉?"
我想了想:"像是坐过山车。"
"习惯就好了。每年都有。"
他顿了顿。
"你那个SQL救场的事,张涛记住了。"
"真的?"
"他刚才跟我说,那个实习生不错,可以培养。"
我愣住了。
"他之前......怎么看我?"
"之前?"老李想了想,"之前他可能都没注意过你。"
"现在呢?"
"现在不一样了。你在他眼里,不再是'那个实习生',是'林默'。"
老李喝了一口水。
"这就是你的第一个功劳。记住这种感觉。"
那天晚上回到出租屋,我躺在床上,睡不着。
窗外是城中村的灯火。远处传来麻将的声音,还有谁家在放电视剧。
我想起三个月前,第一天来公司的样子。
那时候我连透视表都不会。
现在我能写SQL救场了。
我不知道这算不算进步。
但我知道一件事:有些能力,你平时觉得没用,但关键时刻能救命。
老李说得对。
闲的时候学的东西,忙的时候会有用。
(第三章完)
本章知识点
- SQL基础:SELECT, WHERE, GROUP BY, ORDER BY
- 子查询:把一个查询的结果作为另一个查询的输入
- LEFT JOIN:多表关联
- CASE WHEN:条件分支,用于数据分层
本章金句
- "谁喊得最凶,谁就排前面"
- "主动比被动强"
- "闲的时候学的东西,忙的时候会有用"