《数据十年》第三章:适应

0 阅读10分钟

第三章:适应

闲中不放过,忙处有受用。 —— 《菜根谭》


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写的,三百多行。

说实话,我看不太懂。

但我看懂了注释。注释写得很详细,记录了每一步的逻辑:

  1. 从订单表取用户的消费金额
  2. 从浏览表取用户的浏览次数
  3. 从收藏表取用户的收藏商品数
  4. 把三张表关联起来
  5. 根据阈值把用户分成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:条件分支,用于数据分层

本章金句

  • "谁喊得最凶,谁就排前面"
  • "主动比被动强"
  • "闲的时候学的东西,忙的时候会有用"