咱们工作中写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源代码