关于数据开发思维: 批处理的算法抽象
今天看到一个关于自动化生成SQL的视频(加班1周,我被这个需求搞疯了!!!),视频up需要实现一个中国式复杂报表,因SQL逻辑复杂且代码较长,up通过JSON配置方式实现了自动生成SQL代码,从而按期完成工作。粗看没啥毛病,但是只要稍微多想想,就会发现很多问题:
架构奇怪,为啥要在单个查询实现?up所需结果是个报表,一般而言需要在大数据平台中批处理计算好落表存储,可视化工具只需查询结果表。如果需要查询结果实时更新,也可以通过流处理软件实现,查询的也是流处理计算好的表,而不是对Rawdata进行一遍又一遍的读取;
项目层面考虑,拼接SQL的做法并不鲜见,orm框架已经很成熟,这个项目只能解决此类单一场景的问题,考虑投入产出,真的有必要造轮子吗?
核心功能上,该项目的JSON转SQL功能,本质上是在实现模板引擎,现成的工具也非常多,为什么要从零写一个?
解决方案分析,自动生成SQL是最优解吗?3000行的SQL性能如何?能否对问题进行抽象再写代码?
如此这般,虽然做的很好,请以后别这么做了。

我们不妨尝试一下手写SQL的方案,先分析一下需求:

统计指标分布在不同的数据源中,需要将基本信息表、三连表、学习表、电脑表关联拼接到一起
报表行包含了汇总和透视,不同行的统计条目不同
全校指标列需要局部上卷
全表指标均需要计算环比
我们尝试来逐步分解并实现它:
第一步:补齐宽表,将基本信息、三连、学习、电脑等表JOIN起来,生成一个包含指标所需原始字段的宽表,这一步的实现代码略过,后续我们将直接在这个表的基础上实现;
我们假设这个表的结构如下:
通过excel公式,我们准备随机数据导入到表中:
链接: https://pan.baidu.com/s/1QWF8o15CRjKfxXVQUYk0Fg 提取码: 9aj9
第二步:生成包含不同条目和类别汇总的透视表:
这里逻辑稍微解释下:
students表将学生特征向量化,准备一个长度为枚举数量的向量(代码中是2+2+3),每一位按照特征值标记为0或1,存放在feature字段中,这种编码方式叫做one-hot编码
template表是报表的模板,记录每行需要的特征和排序
二者取笛卡尔积关联后,通过布尔向量与“&”运算,符合特征的行结果将不为零,我们将这些行标记为1,否则标记为0,实际汇总指标时,只需要将该因数和指标相乘再作汇总即可
结果使用模板的seq列排序,就实现整个报表70%以上的需求了。这时候你可能已经发现,“年级”我并没有增加汇总行,通过修改版本模板和特征也可以用上述方法实现。事实上,这种方式模板表的量级不宜过大,需要注意数据量膨胀带来的性能下降问题,因此在实际开发中,一般会放在后续步骤处理。
该步骤的结果如下:

第三步: 实现局部上卷和环比。一般会使用窗口函数sum() over()的方式从而实现局部上卷,环比的情况也可以通过lag()实现分组排序的上一条记录。由于我建表时忘记加日期字段,就到此为止吧,这部分也不难。

我们常常因为目标明确,采用了过程化的解决方案,而忽略了数据思维。作为团队技术Leader,我也是常常在Review新人代码时,看着代码迷惑不解,然后若有所思,最后陷入反思:技术人掌握着大量的算法抽象,为啥他们只看到了处理过程。
视频中up主的加班造轮子的过程固然可歌可泣,但我们显然有更好的办法来降低复杂度(不只是计算层面,架构尤甚)。通过构造模板和布尔运算,我们几乎用zero effort就脱离了堆叠查询的困境。
数据岗位的面试中,往往会用“股票的最大连续涨停天数”(变体如“APP用户最大连续签到天数”)等SQL题来考察候选人的SQL水平,尽管该题的通过率不高,但候选人只要被问过一遍,应该以后都有“标准答案”了。如果本着寻找高效工具人的角度,这类面试题就够了。相比于固定套路,我更愿意考虑算法抽象能力和解决方案设计能力强的候选人。
技术人掌握着工具和方法,往往容易陷入有一把锤子后看什么都像钉子的困境。淘汰掉众多的低级错误和技能、认知限制的案例, Odoo复杂审批流终极实现 - 01 源起不适 堪称典型。如果你看完这个视频,你也就明白算法抽象能力和解决方案设计能力远比代码能力重要的原因了。