一个比 SQLite 还好用的数据库神器
数据源支持
复杂计算
select * from (select *, row_number() over (partition by Client order by Amount desc) as row_number from Orders) where row_number<=3
select max(continuousdays)
from (
select count(*) continuousdays
from (
select sum(risingflag) over (order by day) norisingdays
from (
select day, case when price>lag(price) over (order by day) then 0 else 1 end risingflag
from tbl
)
) group by norisingdays
)
SQL 很难直接表达连续上涨的概念,只能换个方法变相实现,即通过累计不涨天数来计算连续上涨天数,这种方法技巧性强,编写难度大且不易理解。而且 SQL 难以调试,导致维护困难。
with A as
(select client,amount,row_number() over (order by amount) ranknumber
from sales)
select client,amount
from (select client,amount,sum(amount) over (order by ranknumber) acc
from A)
where acc>(select sum(amount)/2 from sales)
order by amount des
SQL 很难处理恰好要过线的客户,只能换个方法变相实现,即计算销售额从小到大的累计值,反过来找出累计值不在后一半的客户。这种方法技巧性强,代码冗长,而且难以调试。
流程处理
Class.forName("com.esproc.jdbc.InternalDriver");
Connection conn =DriverManager.getConnection("jdbc:esproc:local://");
Statement statement = conn.createStatement();
ResultSet result = statement.executeQuery("=T(\"D:/Orders.csv\").select(Amount>1000 && like(Client,\"*s*\"))");
SPL 支持数据持久化,可以将数据保存到自有数据格式(集文件)中,比如批量新增记录:
A | |
1 | =create(OrderID,Client,SellerID,Amount,OrderDate) |
2 |
=A1.record([201,"HDR",9,2100.0,date("2021-01-01"), 203,"APPLE",4,1900,date("2021-01-03")]) |
3 | =file("d:/Orders.btx").export@ab(A2) |
除了直接持久化,也可以先处理内存中的序表(SPL 的结构化数据对象,可类比为 SQL 结果集),再将序表覆盖写入集文件,具体做法是将 export@ab 改为 export@b。这种方式性能不如 SQLite,但小微型应用的数据量普遍不大,覆写的速度通常可接受。
组表是 SPL 的另一种自有数据格式,支持高性能批量增删改,适用于大数据量高性能计算(这不是本文重点)。
除了自有格式,SPL 也可以将数据保存到 csv 文件中,只要把 A3 改为:
file("d:/Orders.csv").export@tc(A2)
SPL 有足够的计算能力,支持各类 SQL 式计算,包括分组后计算(窗口函数):
A | B | |
1 | =Orders.new(Client,Amount) | // 选出部分字段 |
2 | =Orders.select(Amount>1000 && like(Client,\"*s*\")) | // 模糊查询 |
3 | = Orders.sort(Client,-Amount) | // 排序 |
4 | = Orders.id(Client) | // 去重 |
5 | =Orders.groups(year(OrderDate):y,Client;sum(Amount):amt).select(amt>3000) | // 分组汇总 |
6 | =[Orders.select(Amount>3000),A1.select(year(OrderDate)==2009)].union() | // 并集 |
7 | =Orders.groups(year(OrderDate):y,Client;sum(Amount):amt).select(like(Client,\"*s*\")) | // 子查询 |
8 | =A5.derive(amt/amt[-1]-1: rate) | // 跨行 |
SPL 提供了基本的 SQL 语法,比如分组汇总:
$select year(OrderDate) y,month(OrderDate) m, sum(Amount) s,count(1) c from {Orders} Where Amount>=? and Amount<? ;arg1,arg2
数据源支持
SPL 读取 csv 文件只需一步,在 Java 里嵌入下面的 SPL 代码:T("d:/Orders.csv").select(Amount>2000 && Amount<=3000)
json(file("d:/xml/emp_orders.json").read()).select(Amount>2000 && Amount<=3000)
json(httpfile("http://127.0.0.1:6868/api/orders").read()).select(Amount>2000 && Amount<=3000)
XML 文件:
A | |
1 | =file("d:/xml/emp_orders.xml").read() |
2 | =xml(A1,"xml/row") |
3 | =A2.select(Amount>1000 && Amount<=2000 && like@c(Client,"*business*")) |
A | |
1 | =ws_client("http://127.0.0.1:6868/ws/RQWebService.asmx?wsdl") |
2 | =ws_call(A1,"RQWebService":"RQWebServiceSoap":"getEmp_orders") |
3 | =A2.select(Amount>1000 && Amount<=2000 && like@c(Client,"*business*")) |
跨源计算
SPL 开放性较好,可以直接计算多种数据源,这些数据源可以和 SPL 集文件进行跨源计算。比如,对集文件和 csv 进行内关联分组汇总:
join(T("d:/Orders.btx"):o,SellerId; T("d:/Emp.csv"):e,EId).groups(e.Dept;sum(o.Amont))
join@1(json(httpfile("http://127.0.0.1:6868/api/orders").read()):o,SellerId; T("d:/Emp.csv"):e,EId)
A | |
1 | =Orders=json(httpfile("http://127.0.0.1:6868/api/orders").read()) |
2 | =Employees=T("d:/Emp.csv") |
3 | =join@1(Orders:o,SellerId;Employees:e,EId) |
任意数据源的持久化
file("d:/Orders.csv").export@t(A2) //csv文件
file("d:/Orders.xlsx").xlsexport@t(A2) //xls文件
file("d:/Orders.json").write(json(A2)) //json文件
A | B | |
1 | =connect("orcl") | / 连接外部 oracle |
2 | =T=A1.query("select * from salesR where SellerID=?",10) | / 批量查询,序表 T |
3 | =NT=T.derive() | / 复制出新序表 NT |
4 | =NT.field("SELLERID",9) | / 批量修改新序表 |
5 | =A1.update(NT:T,sales;ORDERID) | / 持久化 |
数据库的持久化以序表为媒介,其优点相当明显:函数 update 可自动比对修改(增改删)前后的序表,能够方便地实现批量数据地持久化。
计算能力
Orders.group(Client).(~.top(3;Amount))
A | |
1 | =tbl.sort(day) |
2 | =t=0,A1.max(t=if(price>price[-1],t+1,0)) |
再看个例子,求销售额占到一半的前 n 个客户:
A | B | |
2 | =sales.sort(amount:-1) | / 销售额逆序排序,可在 SQL 中完成 |
3 | =A2.cumulate(amount) | / 计算累计序列 |
4 | =A3.m(-1)/2 | / 最后的累计即总额 |
5 | =A3.pselect(~>=A4) | / 超过一半的位置 |
6 | =A2(to(A5)) | / 按位置取值 |
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 | =T=db.query@x("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 | … |
上述代码之外,SPL 还有更多针对结构化数据的流程处理功能,可进一步提高开发效率,比如:每轮循环取一批而不是一条记录;某字段值变化时循环一轮。
Class.forName("com.esproc.jdbc.InternalDriver");
Connection conn =DriverManager.getConnection("jdbc:esproc:local://");
CallableStatement statement = conn.prepareCall("{call queryOrders()}");
statement.execute();
GitHub:https://github.com/SPLWare/esProc
作者|GitHubDaily
本文来自博客园,作者:古道轻风,转载请注明原文链接:https://www.cnblogs.com/88223100/p/A-database-artifact-that-works-better-than-SQLite.html