Skip to main content

SQL 编程新方法

YeongHer

随着各行业对数据分析需求的不断增加,SQL 作为一种古老的数据查询语言,时常与其它编程语言相结合(例如:ORM、模板语言和各类可编程DSL 等),但SQL 始终以字符串的形式存在,弱化了编程属性,使得单元测试、重用、重构和版本管理变得困难,系统工程化也就存在盲区。又因为SQL 的正确性除了语法和基础逻辑的正确, 还依赖最终输出的数据的正确性,更增加系统工程化的复杂度。本文将介绍一种新的方法,用于降低数据分析系统的复杂度,提升数据分析类产品的质量。

1 编程语言发展史

汇编语言:用一些容易理解和记忆的字母、单词来代替一个特定的指令。通过这种方法,人们很容易去阅读和理解。

面向过程编程语言:以数学的方法解决复杂的领域问题,问题被看作一系列需要完成的任务,函数则用于完成这些任务,解决问题的焦点集中于函数。

面向对象编程语言:通过将数据(状态)和数据操作(行为)封装在一起,作为一个相互依存的整体,用来对现实领域问题进行抽象,同时以封装、 继承和多态的基本编程方法,使得系统更灵活、更容易扩展。

C 语言使得编程接近了自然语言的表达,Java 则屏蔽了C/C++ 语言中复杂且容易出错的指针,同时通过JVM 实现了跨平台(目前,已经可以通过LLVM 实现跨平台), Erlang/Scala/Go 则使得并行编程变得便捷。纵观编程语言的发展历史,虽然问题域越来越复杂,但编程语言则变得简单,使得程序员只需要面对实际的问题域, 尽可能忽略底层技术。

然而,SQL 自1986 年被标准化以来,只是不断的增加新特性,从未产生较大的变革,一直以字符串的形式附属在编程语言下。当下各行业对数据的依赖越来越强, 数据分析的复杂度来越来越高。整个行业依然以编程的形式适应着问题域的变化,本质上也是在隔靴搔痒,未能从源头解决问题,将底层技术对数据分析师 / 数据工程师屏蔽。

2 SQL 的底层编程技术

关联关系:SQL 中表与表之间的关联关系是查询的基础,也是SQL 编程中频繁使用,但关联关系往往在关系建模时就已经确定的。在不同场景的数据查询时, 关联关系不会发生变化,然而,每次查询时,需要重复指定关联关系。

2.1 Chasm & Fan Trap:断层和扇形陷阱是关系型数据库出现后就存在的反直觉的设计,也是数据查询时无法避免问题。 通常这类错误的处理方法是子查询,也就是不断的数据变形(Data Reshaping),使得统计维度一致后再进行连接。同时,也导致SQL 的结构变得复杂、可读性差。

2.2 不同数据库SQL 方言:各大数据库厂商基于自身的理论和技术,创造了多种不同形式的SQL 语法,但SQL 查询的本质并没有发生变化, 只是为个性化特性增加了一些函数,或一些语法上小变化。虽然大都数企业,数据库选型不经常变化,有时,随着业务不断发展,不得不重新选型。

2.3 数据分析模式:数据分析方法经过数年的迭代,沉淀了大量通用的分析方法,暂且定义为数据分析模式,例如:同环比分析、留存分析等分析方法。 这类抽象化分析方法,完全可以进行封装和重用,但由于SQL 总是以字符串的形式存在,限制了扩展性。

2.4 跨表计算:复杂业务中,统计指标通常涉及多张表的字段,有时还会以不同聚合函数聚合后进行算术运算。这类计算方法会产生大量子查询和重复的表连接,有时, 通用的维度会被反复连接多次。

上述SQL 的底层技术都属于抽象的编程方法,也是数据库工程必须要掌握的基础技能,类似C 语言中的指针,Java 编程中的线程等抽象的编程概念。编程语言经过数十年的迭代, 除了提升工程化能力,也降低了编程的门槛,使得程序员的学习成本越来越低。但SQL 随着业务的发展变得越来越复杂,容易出错,且开发效率低下。

数据分析对于数据库而言,只是一个业务场景,数据库除了满足分析需求,同时还要满足数据写入和更新的需求,与业务系统关联的非常紧密。然而,数据分析面向的是抽象化的关系模型, 相比数据的写入和更新而言,受业务逻辑的影响相对较小。因此,基于数据分析场景设计一门高级语言就变得有价值,屏蔽上述底层技术变得有价值,使得数据分析时,无需关注底层技术细节, 聚焦于自身业务。

3 常见指标定义方法(聚合方式)

3.1 简单聚合:最常见的聚合函数有MINCOUNTSUM等,例如:SUM(order_details.quantity)COUNT(customers.customer_name)

3.2 聚合计算:与上面的简单聚合类似,但增加了额外的数学运算符。 例如:客单价计算方法:销售额 / 订单数量

SUM(order_details.quantity * order_details.unit_price) / COUNT(orders.order_id)

3.3 多层聚合:与简单聚合类似,区别在于聚合函数存在嵌套的情况,例如:计算月平均销售额(实际存储的数据为单笔订单),以Agile Query 的函数为示例。

AVG(GROUP_SUM(order_details.quantity * order_details.unit_price, orders.order_date))

3.4 多维度聚合

3.5 基于窗口函数聚合

4 聚合函数分类

5 高级分析型语言 FlatQL