当前位置:网站首页>SQL后计算的利器
SQL后计算的利器
2022-08-04 08:45:00 【林小鹿@】
现代应用开发中,通常只用SQL实现简单的数据存取动作,而主要的计算过程和业务逻辑直接在应用程序中实现,主要原因在于:
- 过于复杂的SQL很难调试、编写、阅读、修改。
- SQL有方言特征,大量使用SQL后,会导致程序很难移植。
- 架构方面要求业务逻辑在应用中实现,而不能依赖于数据库,否则耦合性过高。
- 有些计算SQL不擅长,包括复杂的集合计算、有序计算、关联计算、多步骤计算,经常也需要移到数据库外实现。
- 实现流程控制时,因为更难移植、耦合性更高、影响数据安全,不方便使用存储过程。
此外,还有涉及多数据库和非数据库的场景,也无法使用SQL完成计算任务,只能在外部完成。
这样,就要在应用程序中实现SQL后计算任务。
SQL返回的数据一般都是结构化数据,那么好的SQL后计算技术也要有方便的结构化数据对象,能够进一步计算和处理返回的数据;提供丰富的库函数,拥有不亚于SQL的计算能力;最好还能支持循环和判断语法以实现流程控制。特别地,SQL后计算技术要用在应用程序中,要易于被集成。
Java是重要的开发语言,但JDK提供的方法过于基础,虽然能实现SQL后计算,但开发效率很低。
ORM是Java中用来实现SQL后计算的常见方案。但几种较流行的ORM都缺乏专业的结构化数据对象,不支持动态数据结构。虽然可以利用Java实现流程控制,但难以进行灵活的计算。这些ORM技术的计算能力还远不如SQL,提供的计算函数非常有限,用Java硬写的现象仍然非常普遍。
Stream的链式编程比ORM的HQL更加面向对象,且有Lambda语法的加成,也常被用于SQL后计算,有些ORM还能直接生成Stream对象。但Stream同样没有专业的结构化数据对象,不支持动态数据结构。此外,Stream的计算能力也较差,甚至不如ORM,即使排序、分组汇总、关联这样的基础计算,也要辅以大量编码。
Kotlin基于JVM,且在链式编程和Lambda语法上对Stream进行了一系列改进,也可以用于SQL后计算。但因为编译型语言的底层,Kotlin只能对Stream小幅微调,重大缺点一个没少。
Python Pandas有较强大的结构化数据处理能力,有时也可以用于SQL后计算,但因为缺乏易用的接口,很难被Java集成,很少出现在正式项目中。
esProc SPL是更好的SQL后计算技术。
专业的结构化数据对象
SPL是JVM下的开源结构化数据计算引擎,内置专业的结构化数据对象序表,可以和数据库表/记录方便地互转,支持动态数据结构,提供了灵活易用的访问方法、维护方法、计算函数。序表专业性强,为数据计算和流程控制提供了有力的底层支撑,可以方便地实现SQL后计算中的各类业务逻辑。
直接的数据库交换方法,可以在数据库表(SQL结果集)和SPL序表之间进行互转。
比如,使用query函数执行SQL,生成单条记录序表:
A | B | |
1 | =connect("orcl") | //连接数据库 |
2 | .query("select * from sales where orderid=?",201)=r=A1 | //查询单条记录 |
3 | =db.close() | //关闭数据库连接 |
如果SQL返回多条记录,则自动生成多条记录序表:
=T=A1.query(“select * from salesR where SellerID=?”,10)
反过来也简单,用update函数就可以将序表记录批量地持久化到数据库。比如,原序表为T,经过多条件记录的增删改之后的序表为NT,将两者的变更结果统一写入数据库:
=A1.update(NT:T,sales;ORDERID)
灵活的序表访问方法,可以按字段名或记录号自由地访问序表。
取序表的第3条记录:T(3)
取后3条记录:T.m([-1,-2,-3])
取记录的字段值:T(3).Amount*0.05
取一列,返回集合:T.(Amount)
取几列,返回集合的集合:T.([CLIENT,AMOUNT])
先按字段名取再按记录序号取:T.(AMOUNT)(3)
先按记录序号取再按字段名取:T(3).AMOUNT
易用的序表维护方法,可以对单条或多条记录记录进行统一的增删改操作。
追加记录:T.insert(200,“APPL”,10,2400.4,date(“2010-10-10”))
修改记录:T(3).Amount = T(3). Amount*1.05
删除记录:T.delete(T.select(ORDERID==209 || ORDERID==208))
丰富的序表计算函数,可进行完整的SQL式计算。
过滤:T.select(Amount>1000 && Amount<=3000 && like(Client,“bro”))
排序:T.sort(-Client,Amount)
去重:T.id(Client)
汇总:T.max(Amount)
分组汇总后过滤: T.groups(year(OrderDate),Client; avg(Amount):amt).select(amt>2000)
关联:join(Orders:o,SellerId ; Employees:e,EId).groups(e.Dept; sum(o.Amount))
交集:T1.id(Client) ^ T2.id(Client)
TopN:T.top(-3;Amount)
分组topN:T.groups(Client;top(3,Amount))
支持动态数据结构,可根据上一步的计算结果推断出新数据结构,并自动生成新序表,新序表可直接进行计算。比如先分组汇总,再过滤,最后排序:
T.groups(SellerId, Client; sum(Amount):amt, count(1):cnt).select(amt>10000 && amt<=30000 && like(Client,“*bro*”)).sort(amt)
使用支持动态数据结构的序表,开发者可以更加关注计算本身,而不是思考如何事先定义结果集。这样的编码风格不仅简短易懂,而且更符合自然思维,开发效率可以显著提升。在多步骤的复杂业务逻辑中,动态数据结构带来的优势更加明显。
强大的结构化数据计算能力
SPL提供了多种方便易用的语法,内置大量功能强大的函数,可以简化复杂的有序运算、集合运算、分布计算、关联计算。很多用SQL和存储过程难以表达的计算,用SPL都可以轻松实现。
函数选项、层次参数等方便的语法,功能相似的函数可以共用一个函数名,只用函数选项区分差别,比SQL更加灵活方便。比如select函数的基本功能是过滤,如果只过滤出符合条件的第1条记录,可使用选项@1:
[email protected](Amount>1000)
并行过滤,适合数据量较大的情况,使用选项@m:
[email protected](Amount>1000)
二分法排序,即对有序数据用二分法进行快速过滤,使用@b:
[email protected](Amount>1000)
有序分组,即对分组字段有序的数据,将相邻且字段值相同的记录分为一组,使用@b:
[email protected](Client;sum(Amount))
函数选项还可以组合搭配,比如:
[email protected](Amount>1000)
结构化运算函数的参数有些很复杂,比如SQL就需要用各种关键字把一条语句的参数分隔成多个组,但这会动用很多关键字,也使语句结构不统一。SPL使用层次参数简化了复杂参数的表达,即通过分号、逗号、冒号自高而低将参数分为三层:
join(Orders:o,SellerId ; Employees:e,EId)
内置大量日期函数和字符串函数,在数量和功能上远远超过其他技术甚至SQL,同样的运算代码量更短。比如:
时间类函数,日期增减:elapse(“2020-02-27”,5) //返回2020-03-03
星期几:[email protected](“2020-02-27”) //返回5,即星期4
N个工作日之后的日期:workday(date(“2022-01-01”),25) //返回2022-02-04
字符串类函数,判断是否全为数字:isdigit(“12345”) //返回true
取子串前面的字符串:[email protected](“abCDcdef”,“cd”) //返回abCD
按竖线拆成字符串数组:“aa|bb|cc”.split(“|”) //返回[“aa”,“bb”,“cc”]
SPL还支持年份增减、求年中第几天、求季度、按正则表达式拆分字符串、拆出SQL的where或select部分、拆出单词、按标记拆HTML等功能。
简化复杂的有序运算。涉及跨行的有序运算,通常都有一定的难度,比如比上期和同期比。SPL使用"字段[相对位置]"引用跨行的数据,可显著简化代码,还可以自动处理数组越界等特殊情况,经常比SQL更方便。比如,追加一个计算列rate,计算每条订单的金额增长率:
=T.derive(AMOUNT/AMOUNT[-1]-1: rate)
综合运用位置表达式和有序函数,很多SQL难以实现的有序运算,都可以用SPL轻松解决。比如,根据考勤表,找出连续 4 周每天均出勤达 7 小时的学生:
A | |
1 | =connect("mysql") |
2 | [email protected]("SELECT SID,ATTDATE,DURATION,null AS W FROM STUTEST WHERE DURATION>=7 ORDER BY SID,ATTDATE").run([email protected](ATTDATE)) |
3 | [email protected](SID;[email protected](W;count(~):CNT).select(CNT==7)[email protected](W-W[-1]!=7).max(~.len()):weeks) |
4 | =A3.select(weeks>=4).(SID) |
简化复杂的集合运算,SPL序表的集合化更加彻底,配合灵活的语法和强大的集合函数,可大幅简化复杂的集合计算。比如,在各部门找出比本部门平均年龄小的员工:
A | |
1 | …//省略序表Employees的生成过程 |
2 | =Employees.group(DEPT; (a=~.avg(age(BIRTHDAY)),~.select(age(BIRTHDAY)<a)):YOUNG) |
3 | =A2.conj(YOUNG) |
计算某支股票最长的连续上涨天数:
A | |
1 | …//省略序表AAPL的生成过程 |
2 | =a=0,AAPL.max(a=if(price>price[-1],a+1,0)) |
SPL可以方便地实现分步计算,序表的集合化更加彻底,可以用变量方便地表达集合,适合多步骤计算,很多SQL难以表达的集合运算,用SPL都可以轻松实现。比如,找出销售额累计占到一半的前n个大客户,并按销售额从大到小排序:
A | B | |
1 | //省略取数据的过程 | |
2 | =A1.sort(amount:-1) | /销售额逆序排序,可在SQL中完成 |
3 | =A2.cumulate(amount) | /计算累计序列 |
4 | =A3.m(-1)/2 | /最后的累计即总额 |
5 | =A3.pselect(~>=A4) | /超过一半的位置 |
6 | =A2(to(A5)) | /按位置取值 |
简化复杂的关联计算。序表的专业性体现在多方面,其中之一是支持对象引用的形式表达关联,开发者可以通过点号直观地访问关联表,从而提高开发效率。很多SQL难以表达的关联计算,用SPL都可以轻松实现。比如,根据员工表计算女经理的男员工:
=employees.select(gender:“male”,dept.manager.gender:“female”)
灵活的流程控制能力
SPL提供了灵活易用的分支判断语句、循环语句,配合专业的结构化数据对象,可以方便地实现各类业务逻辑。
分支判断语句:
A | B | |
2 | … | |
3 | if T.AMOUNT>10000 | =T.BONUS=T.AMOUNT*0.05 |
4 | else if T.AMOUNT>=5000 && T.AMOUNT<10000 | =T.BONUS=T.AMOUNT*0.03 |
5 | else if T.AMOUNT>=2000 && T.AMOUNT<5000 | =T.BONUS=T.AMOUNT*0.02 |
循环语句:
A | B | |
1 | =db=connect("db") | |
2 | [email protected]("select * from sales where SellerID=? order by OrderDate",9) | |
3 | for T | =A3.BONUS=A3.BONUS+A3.AMOUNT*0.01 |
4 | =A3.CLIENT=CONCAT(LEFT(A3.CLIENT,4), " co.,ltd.") | |
5 | … |
与Java的循环类似,SPL还可用break关键字跳出(中断)当前循环体,或用next关键字跳过(忽略)本轮循环,不展开说了。
流程控制语句配合序表,可以用统一的语法风格实现业务逻辑,包括数据库读写、事务处理、流程处理、数据计算。比如,根据一定的规则计算奖金:
A | B | C | |
1 | [email protected]("dbName") | /连接数据库,开启事务 | |
2 | [email protected]("select sum(Amount) from sales where sellerID=? and year(OrderDate)=? and month(OrderDate)=?", p_SellerID,year(now()),month(now())) | /查询当月销售额 | |
3 | =if(A2>=10000 :200, A2<10000 && A2>=2000 :100, 0) | /本月累计奖金 | |
4 | =p_Amount*0.05 | /本单固定奖金 | |
5 | =BONUS=A3+A4 | /总奖金 | |
6 | =create(ORDERID,CLIENT,SELLERID,AMOUNT,BONUS,ORDERDATE) | /创建订单的数据结构 | |
7 | =A6.record([p_OrderID,p_Client,p_SellerID,p_Amount,BONUS, date(now())]) | /生成一条订单记录 | |
8 | >[email protected](A7,sales;ORDERID) | /尝试写入库表 | |
9 | =db.error() | /入库结果 | |
10 | if A9==0 | >A1.commit() | /成功,则提交事务 |
11 | Else | >A1.rollback() | /失败,则回滚事务 |
12 | >db.close() | /关闭数据库连接 | |
13 | return A9 | /返回入库结果 |
优化体系结构
SPL支持JDBC接口,代码可外置于Java,耦合性更低。SPL具有解释执行的特性,支持库外计算和代码移植,支持跨库跨源计算,在SQL后计算中可提供良好的架构性。
SPL提供了易用的JDBC接口,可被Java代码无缝集成。比如,将前面的SPL代码存为脚本文件,再在Java中以存储过程的形式调用文件名:
Class.forName("com.esproc.jdbc.InternalDriver");
Connection connection =DriverManager.getConnection("jdbc:esproc:local://");
Statement statement = connection.createStatement();
ResultSet result = statement.executeQuery("call writeBonus()");
SPL代码外置于Java,通过文件名被调用,既不依赖数据库,也不依赖Java,业务逻辑和前端代码分离,耦合性低。
解释执行和热切换。业务逻辑数量多,复杂度高,变化是常态。良好的系统构架,应该有能力应对变化的业务逻辑。ORM的本质是Java代码,需要先编译再执行,一般都要停机才能部署,应对变化的业务逻辑时非常繁琐。SPL是基于Java的解释型语言,无须编译就能执行,脚本修改后立即生效,支持不停机的热切换,适合应对变化的业务逻辑。
方便代码移植。SPL通过数据源名从数据库取数,如果需要移植,只要改动配置文件中的数据源配置信息,而不必修改SPL代码。SPL支持动态数据源,可通过参数或宏切换不同的数据库,从而进行更方便的移植。为了进一步增强可移植性,SPL还提供了与具体数据库无关的标准SQL语法,使用sqltranslate函数可将标准SQL转为主流方言SQL,仍然通过query函数执行。
方便管理运营。由于支持库外计算,代码可被第三方工具管理,方便团队协作;SPL脚本可以按文件目录进行存放,方便灵活,管理成本低;SPL对数据库的权限要求类似Java,不影响数据安全。
跨库和跨源计算。SPL序表可以统一读取各类数据源(含RDB),可以用统一的代码计算各类数据源,可以方便地实现跨库计算。比如,对MySQL和Oracle进行内关联:
A | |
1 | =mysql1.query("select SellerId, sum(Amount) subtotal from Orders group by SellerId") |
2 | =orcl.query("select EId,Name from employees") |
3 | =join(A1:O,SellerId; A2:E,EId).new(O.Name:name, O.Dept:dept, E.subtotal:amt) |
SPL支持多种非RDB数据源,可进行RDB和非RDB之间的混合计算。包括txt\csv\xls等文件,MongoDB、Hadoop、redis、ElasticSearch、Kafka、Cassandra等NoSQL,以及WebService XML、Restful Json等多层数据。
ORM以及Stream/Kotlin缺乏专业的结构化数据对象和运算能力,Python Pandas难以被Java集成。SPL内置专业的结构化数据对象,具有强大的结构化数据计算能力和灵活的流程控制能力,可以方便地实现各类业务逻辑。提供了易用的JDBC接口,可以被Java方便地集成。还有更多结构性优势,包括耦合性低、解释执行和热切换、库外计算,以及跨库和跨源计算。
SPL资料
边栏推荐
- Recommend several methods that can directly translate PDF English documents
- 图的基本概念
- 占位,稍后补上
- IDEA引入类报错:“The file size (2.59 MB) exceeds the configured limit (2.56MB)
- 阿里云的数据库系统怎么升级更新的www.zgysffm.com怎么加快访问速度?
- 【电脑录制屏】如何使用bandicam录游戏 设置图文教程
- powershell和cmd对比
- DeLighT:深度和轻量化的Transformer
- 经典动态规划问题的递归实现方法——LeetCode39 组合总和
- recursive thinking
猜你喜欢
高等代数_证明_幂等矩阵一定能够相似对角化
C语言strchr()函数以及strstr()函数的实现
【论文笔记】Delving into the Estimation Shift of Batch Normalization in a Network
layout manager
sql在字段重复时 对某个字段根据最新时间取数
2022-08-02 分析RK817 输出32k clock PMIC_32KOUT_WIFI给WiFi模块 clock 注册devm_clk_hw_register
线程安全问题
【Attention】Dual Attention(DANet) & Fully Attention(FLA)
【电脑录制屏】如何使用bandicam录游戏 设置图文教程
recursive thinking
随机推荐
MMDetection finetune
新特性解读 | MySQL 8.0 在线调整 REDO
微信消息从发送到接收,经历了什么?如何防止丢包
字符流与字节流的区别
The sorting algorithm including selection, bubble, and insertion
binder通信实现
JNI学习1.环境配置与简单函数实现
Unity3D data encryption
layout manager
The separation configuration Libpq is supported, speaking, reading and writing
安装GBase 8c数据库集群时,报错误码:80000306,显示Dcs cluster not healthy。怎么处理错误呢?
经典递归回溯问题之——解数独(LeetCode 37)
recursive thinking
redis分布式锁的实现
技术实现 | 图像检索及其在高德的应用
Cross-species regulatory sequence activity prediction
DWB主题事实及ST数据应用层构建,220803,,
最近的一些杂感-20220731
【虚幻引擎UE】UE5实现WEB和UE通讯思路
leetcode 22.7.31(1)两数之和 (2)整数除法