乐趣区

关于sql:SQL-嵌套-N-层太长太难写怎么办

咱们工作中写 SQL 解决数据是粗茶淡饭,不论是利用内数据处理还是长期查问剖析都能够用 SQL 实现,绝对其余技术(如 Java 等高级语言)也更简略。不过,SQL 的简略只限于简略需要,有些简单计算场景 SQL 写起来却很难,嵌套 N 层以至于达到几百上千行,说 SQL 代码长度时通常不会以行计而是以 KB 计。这种状况并不少见,置信常常写 SQL 的小伙伴并不生疏。

为什么会呈现这种状况呢?在 http://c.raqsoft.com.cn/artic… 里详细分析了这个问题。尽管 SQL 比其余数据处理技术更简略,但仍存在一些短板(如不足有序反对、汇合化不彻底、没有高级语言的对象援用机制等),导致简单计算用 SQL 表达出来很繁琐。这种既简单又很长的 SQL 会带来很多问题。

长 SQL 有什么危害?

简单长 SQL 给咱们带来的第一个困扰是难写。

其实,代码长自身还不是大问题,很多工作步骤多也就会写得长,但并不难。而 SQL 的长,经常是随同着难,而且会随着长度减少变得异样难。SQL 在解题时很绕(思维形式上的难),你没法依照失常思路去施行算法,明明想出一个好的解法,简略一二三四步就实现了,但用 SQL 就要绕来绕去,不嵌套几层写个几十上百行如同体现不出你程度一样。而且不光嵌套子查问,简单 SQL 还随同多表关联和各种过滤条件,写的时候要放弃头脑异样苏醒,一旦写错不仅很难定位,有时还会把数据库跑死。这当然跟 SQL 不提倡过程(CTE 语法提供了肯定反对)无关,一个工作写 100 行代码,分成 100 个语句还是只有 1 个语句,其复杂度齐全不是一个层面的。

另外,写代码还离不开调试,而 SQL 的调试性能堪称难用至极。直到明天各大数据库都没提供像样的调试性能,相比其余高级语言的开发调试环境几乎不忍直视。一句嵌套 N 层的长 SQL 发现写的不对只能把子查问从长语句里一层一层摘出来独自执行调试定位,费时费力,这更增大了 SQL 的书写难度。

难写就意味着开发周期长,咱们写 SQL 都是为前端业务服务的,一个计算需要连写带调试搞个三五天甚至一周,恐怕业务时效性都没了,被怒怼也是有苦难言。

除了难写,简单 SQL 还很难保护。数据处理业务的稳定性往往很差,常常要批改,不过想要批改这些 SQL 恐怕并非易事。别说新人接手,就是作者自己也经常出现过一段时间本人都看不懂的难堪状况。改一个 SQL 跟从新写一个的工夫差不多,这还玩啥。

另外简单 SQL 还会影响数据库移植。尽管数据库移植不常常产生,不过一旦产生就要命。近些年随着业务的高速倒退以及开源数据库的崛起,更换数据库时有发生。这就波及到 SQL 迁徙,咱们晓得数据库都是有方言的,在一个数据库上应用的函数到另外一个数据库未必管用,而且各类数据库对 SQL 规范的反对水平不同(如 Oracle 窗口函数反对的很好,其余数据库就要差很多),这些“非凡的”内容夹杂在简单 SQL 里就很难实现数据库迁徙了。

不管怎样,简单 SQL 都是数据开发人员的噩梦。

怎么办?

SQL 难的问题可不是第一天呈现,大家也都在踊跃寻找解决办法。其实当初很多开发方法曾经不再举荐写简单 SQL,像 ORM 技术算是革掉 SQL 半条命,而微服务等框架更是要求 SQL 仅仅用于实现根本的数据读写,而不能用于做简单计算和业务解决。这些办法的思路很清晰,即把 SQL 的利用局限于根本的读写工作,在利用端实现简单的数据处理和业务逻辑,这样就能够躲避 SQL 的那些问题,架构上也更合乎古代利用的须要。

不必 SQL,那些简单的计算和业务逻辑该用什么技术来做呢?

当然只能是其它程序语言了,简直所有利用级程序语言都能够指挥 SQL 工作,实现这种开发架构不是问题。那么,这个问题是不是就被轻易解决了?

咱们来考查一下这些罕用的技术。

Java

Java 必定是第一抉择,毕竟泛滥应用程序都是 Java 开发的,如果能搞定这些数据处理就会有很多劣势。Java 人造反对过程计算,实现简单计算时尽管代码可能更长,但能够依照失常思维实现算法,这样是不是就能够代替 SQL 了?

No,没有这么简略。

因为 Java 不足业余的结构化数据对象,短少来自底层的无力反对,在实现 SQL 这类的简单计算时并不容易。

结构化数据计算的返回值的构造随计算过程而变,大量的两头后果同样是动静构造,这些都难以当时定义,而 Java 是强类型语言,又必须当时定义数据对象的构造(否则只能用 map 这类操作繁琐的数据对象),这就使 Java 的结构化计算僵化死板,lambda 语法的能力重大受限。解释性语言能够简化参数的定义,函数自身就可指定参数表达式应解释成值参数还是函数参数,而 Java 是编译型语言,难以辨别不同类型的参数,必须设计简单的接口能力实现匿名函数(次要指 lambda 语法),这连 SQL 程序员都不易把握。省略数据对象而间接援用字段(比方写成 ” 单价 * 数量 ”),可显著简化结构化数据计算,但 Java 不足业余的结构化数据对象,目前还无奈反对此类外表简略实则奇妙的语法,这就使 Java 代码简短且不直观(只能写成 ”x. 单价 *x. 数量 ”)。

短少结构化数据计算类库还会导致代码过长,同样的一个分组汇总用 SQL 一句就能写进去改成 Java 就要写几十行,这显然也对简化简单 SQL 有益。这个问题即便在 Java8 减少了 Stream 后也没有明显改善,为了简化运算用 Java 取代 SQL 根本不可能(有些利用用 Java 做数据处理往往是因为架构上的要求,就其简便性上还远不迭 SQL)。

另外,Java 作为编译型语言很难热部署,也就难以及时应答多变的数据计算场景。

Python

间接的 Java 不行,那大热的 Python 怎么样呢?Python+SQL 可比 Java+SQL 容易得多了吧。

确实,Python(次要是 Pandas)提供了丰盛结构化计算类库,计算实现要比 Java 简略很多。不过,理论应用 Pandas 解决数据尤其是简单运算时也会碰到代码很难写的状况。其实,Pandas 原本就不是为结构化数据设计的,它并不是咱们相熟的数据表(一行行数据记录的汇合),而是个矩阵。用来解决结构化数据时,做些过滤合并这些简略运算还好点,碰到分组有序等简单一些运算,就会比拟麻烦了,思路上也常常要绕一下。

这还不是 Python 的次要问题,Python 与利用联合的难点在于其集成性上。

Python 很难与 Java 利用集成!

Python 和 Java 集成次要有两种形式。一种是服务式调用,既然 Python 和 Java 是两套体系,那就独自部署通过服务(网络通信)的形式交互,这种形式要保护两套利用比拟麻烦,还会存在一些平安认证零碎权限等治理问题,而且数据交换的性能也很差,不到万不得已不会采纳这种形式。另一种是应用 Jython 这种解释器(JVM 上的 Python),因为采纳 Java 开发能够很不便与利用集成,但 Jython 又不足足够的计算库(如 Pandas)很难应酬那些简单计算。无论采纳何种形式,都会在利用方面产生微小限度,导致在 Java 利用中用 Python 来实现简单计算的可行性不高。

除非你把整个利用都用 Python 写,然而 Java 的各种企业级能力又用不上了,做做小利用还行吧。

Scala

Scala 的个性与 Python 相似,都提供了 DataFrame 对象,实现简略的结构化数据计算也比较简单,作为高级语言有序、对象属性化等个性反对良好,绝对 Java 在汇合运算上也更简略。Scala 运行于 JVM 之上,天生就容易被 JAVA 集成,这些都是 Scala 的长处。

Scala 的毛病在于应用难度较大,难学更难精,用于简单数据处理与 SQL 相比尤有劣势,也就不必想通过 Scala 简化简单 SQL 了。这大略也是为什么 Spark 要回归 SQL 的起因了吧。而且,Scala 作为编译型语言同样不反对热部署。

SPL

这些高级语言都存在这样那样的毛病,从简化简单 SQL 的角度来看无一能胜任。那还有什么方法呢?

其实,从后面的剖析中咱们显著可能感触到,面向结构化数据计算尤其是简单计算,现有技术(开发语言)都各有优缺点,SQL 善于简单计算但对有序、过程反对不好,Java 恰好反过来反对有序和过程但汇合计算能力很弱。如果能综合这些技术的长处,那简化简单 SQL 的指标也就达成了。

开源 SPL 恰好是这样一个产品。

SPL 全称 Structured Process Language,是一个专门用于结构化数据计算的程序语言。

惯例计算能力

统一的数据类型

SPL 绝对 Java 提供了更业余的结构化数据类型,即序表。和 SQL 的数据表一样,序表是批量记录组成的汇合,具备结构化数据类型的个别性能,能够与 SQL 无缝交互承接 SQL 的返回值。

序表反对所有的结构化计算函数,计算结果也同样是序表,而不是 Map 之类的数据类型。比方对分组汇总的后果,持续进行结构化数据处理。

Orders.groups(year(OrderDate):y; sum(Amount):m).new(y:OrderYear, m*0.2:discount)

丰盛的计算类库

在序表的根底上,SPL 提供了丰盛的结构化数据计算函数,比方过滤、排序、分组、去重、改名、计算列、关联、子查问、汇合计算、有序计算等。这些函数具备弱小的计算能力,毋庸硬编码辅助,就能独立实现计算。

如组合查问:

Orders.select(Amount>1000 && Amount<=3000 && like(Client,"\*bro\*"))

内关联:

join(Orders:o,SellerId ; Employees:e,EId).groups(e.Dept; sum(o.Amount))

SPL 反对简略模式的 Lambda 语法,毋庸定义函数名和函数体,能够间接用表达式当作函数的参数。批改业务逻辑时,也不必重构函数,只须简略批改表达式。SPL 是解释型语言,应用参数表达式时不用明确定义参数类型,使 Lambda 接口更简略。比方计算平方和,想在 sum 的过程中算平方,能够直观写作:Orders.sum(Amount*Amount)。

同时作为解释执行语言的 SPL 还人造反对动静数据结构,能够依据计算结果构造动静生成新序表,特地适宜计算列、分组汇总、关联这类计算。较简单的计算通常都要拆成多个步骤,每个两头后果的数据结构简直都不同。SPL 反对动静数据结构,不用先定义这些两头后果的构造。比方,依据某年的客户回款记录表,计算每个月的回款额都在前 10 名的客户。

Sales2021.group(month(sellDate)).(~.groups(Client;sum(Amount):sumValue)).(~.sort(-sumValue)).(~.select(#<=10)).(~.(Client)).isect()

过程管制与 SQL 协同

除了提供与 SQL 相当的汇合运算能力,SPL 还对过程管制和分步计算提供了良好反对,灵便的分支判断语句、循环语句,配合业余的结构化数据对象,能够不便地实现各类业务逻辑。比方找出销售额累计占到一半的前 n 个大客户,能够这样分步实现:

A
1 =db.query(“select client,sum(amount) amount from sales group by client order by amount desc”)
2 =A1. sum(amount)/2
3 =0
4 =A1.pselect((A3=A3+amount,A3>=A2))
5 =A1(to(A4))

通过 SQL 先实现分组汇总并按汇总值降序排序,而后 SPL 承接 SQL 的计算结果再通过分步形式实现后续计算,SPL 与 SQL 无效联合,这样就很大水平达到了简化简单计算的指标。

在利用中,SPL 很多时候都要与 SQL 交互协同,充分发挥二者的劣势,包含数据库读写、事务处理、流程解决、存储过程调用等。

数据库写入(更新 / 插入):

db.update@u(A7,sales;ORDERID)

执行 DML 语句:

db.execute("insert into DEPARTMENT(DEPT, MANAGER) values(?,?)","TecSupport",9)

调用存储过程:

db.proc({db_proc(?,?),,:101:"o":table1,1:0:"i": })

丰盛的汇合运算能力加上过程计算与流程管制(包含指挥 SQL),这样就取得了 SQL 和 Java 相当的能力,而实现上要比 Java 更简略。

超过 SQL 的能力

SPL 不仅笼罩了 SQL 的所有计算能力,还提供了更弱小语言性能。基于这些个性能够很不便原来在 SQL 中不易实现的运算,简化简单计算可不是开玩笑的。

离散性及其反对下的更彻底的汇合化

汇合化是 SQL 的根本个性,即反对数据以汇合的模式参加运算。但 SQL 的离散性很不好,所有汇合成员必须作为一个整体参于运算,不能游离在汇合之外。而 Java 等高级语言则反对很好的离散性,数组成员能够独自运算。然而,更彻底的汇合化须要离散性来反对,汇合成员能够游离在汇合之外,并与其它数据随便形成新的汇合参加运算。

SPL 兼具了 SQL 的汇合化和 Java 的离散性,从而能够实现更彻底的汇合化。

SPL 很容易表白“汇合的汇合”,适宜分组后计算。比方,找到各科问题均在前 10 名的学生:

A
1 =db.query(“select * from score”) .group(subject)
2 =A2.(~.rank(score).pselect@a(~<=10))
3 =A1.(~(A3(#)).(name)).isect()

有序反对

有序计算是离散性和汇合化的典型联合产物,成员的秩序在汇合中才有意义,这要求汇合化,有序计算时又要将每个成员与相邻成员辨别开,会强调离散性。SPL 兼具汇合化和离散性,人造反对有序计算。

具体来说,SPL 能够按相对地位援用成员,比方,取第 3 条订单能够写成 Orders(3),取第 1、3、5 条记录能够写成 Orders([1,3,5])。

SPL 也能够按绝对地位援用成员,比方,计算每条记录绝对于上一条记录的金额增长率:

Orders.derive(amount/amount[-1]-1)

SPL 还能够用 #代表以后记录的序号,比方把员工按序号分成两组,奇数序号一组,偶数序号一组:

Employees.group(#%2==1)

在有序计算的反对下,再解决结构化数据计算中对于秩序的运算(诸如比上月、比去年同期、前 20%、排名等)就很不便了。

对象援用

SPL 序表的字段能够存储记录或记录汇合,这样能够用对象援用的形式,直观地表白关联关系,即便关系再多,也能直观地表白。比方,依据员工表找到女经理上司的男员工:

Employees.select(gender:"male",department.manager.gender:"female")

更不便的函数语法

提供大量功能强大的结构化数据计算函数原本是一件坏事,但这会让类似性能的函数不容易辨别,无形中进步了学习难度。

SPL 提供了特有的函数选项语法,性能类似的函数能够共用一个函数名,只用函数选项辨别差异。比方 select 函数的基本功能是过滤,如果只过滤出符合条件的第 1 条记录,能够应用选项 @1:

Orders.select@1(Amount>1000)

数据量较大时,用并行计算进步性能,只须改为选项 @m:

Orders.select@m(Amount>1000)

对排序过的数据,用二分法进行疾速过滤,可用 @b:

Orders.select@b(Amount>1000)

函数选项还能够组合搭配,比方:

Orders.select@1b(Amount>1000)

结构化运算函数的参数经常很简单,比方 SQL 就须要用各种关键字把一条语句的参数分隔成多个组,但这会动用很多关键字,也使语句构造不对立。SPL 反对档次参数,通过分号、逗号、冒号自高而低将参数分为三层,用通用的形式简化简单参数的表白:

join(Orders:o,SellerId ; Employees:e,EId)

不便的编辑调试性能

与 SQL 难用的编辑调试性能不同,SPL 提供了简洁易用的开发环境,单步执行、设置断点,所见即所得的后果预览窗口…,使得开发调试效率更高。

SPL 采纳了网格局编码方式,代码不仅人造对齐档次清晰,在施行过程计算时能够间接应用格子名称来援用上一步的计算结果而不用费神定义变量(尽管也反对)十分不便。好用的开发环境天然起到事倍功半的成果,进一步简化简单计算施行难度。

利用集成、低耦合与热切换

SPL 提供了规范利用接口(JDBC/ODBC/RESTful)能够很不便与利用集成。尤其对于 Java 利用能够将 SPL 作为嵌入引擎集成,使得利用自身就具备强数据计算能力。

JDBC 调用 SPL 代码示例:

Class.forName("com.esproc.jdbc.InternalDriver");
Connection conn =DriverManager.getConnection("jdbc:esproc:local://");
Statement st = connection.();
CallableStatement st = conn.prepareCall("{call splscript(?, ?)}");
st.setObject(1, 3000);
st.setObject(2, 5000);
ResultSet result=st.execute();

同时,SPL 采纳解释执行机制,人造反对热切换。这样对于稳定性差、常常须要新增批改的数据处理需要十分敌对。SPL 脚本能够与 Java 程序独立,外置在 Java 之外,批改和保护都能够独立进行,脚本批改上传后就能实时失效,保障利用能够不中断地提供数据服务。

SPL 外置算法不仅能无效升高利用与数据库的耦合性,独立的 SPL 模块还能够进一步升高利用各个模块间的耦合性,使得构造更为清晰,架构也更为正当。

总结一下,SPL 之所以更简略是因为 SPL 相当于联合了 SQL 和其余高级语言(如 Java)的长处,并在此基础上减少了诸多个性让简单计算不再难写,同时能够与利用完满联合使得利用架构更为正当。有了 SPL 的帮忙,咱们置信,将来的某一天上千行的简单 SQL 将不复存在。

## SPL 材料

  • SPL 下载
  • SPL 源代码
退出移动版