
很多小微型应用程序也需要一些数据处理和计算能力,如果集成一个数据库就显得太沉重了,这种情况下 SQLite 是一个不错的选择,它架构简单,集成方便,可持久化存储数据,并提供 SQL 实现计算能力。但是,对于某些较复杂的场景,SQLite 仍有不足之处。SQLite 面对复杂场景的不足数据源支持
SQLite 像个数据库,可以对自有的库文件提供较好的支持,但应用程序有时还要处理其它形式的数据,比如文本文件、Excel、其它数据库、Restful 等 Web 上的数据。SQLite 只支持 csv 文件的读取,不支持其他数据源,除非硬编码。而且,SQLite 虽然支持 csv 文件,但使用过程很繁琐,需要先用命令行创建数据库,再用 create 命令创建表结构,然后用 import 命令导入数据,最后才能用 SQL 查询数据。除了常规结构化数据,现代应用还会经常碰到 Json,XML 等复杂格式的数据。SQLite 有计算 Json 串的能力,但不支持直接读取多层数据源,包括 Json 文件 /RESTful,需要硬写代码,或再借助第三方类库,拼成 insert 语句插入数据表,代码很繁琐。SQLite 也不能计算 XML 串,更不能读取 XML 文件 /WebService。应用程序有时需要把数据写成格式通用的文件,以便输出、转移或交换,有时候还要把数据主动写入其他数据源。但 SQLite 只能将数据持久化到自有的库文件,不能直接写入外部数据源,包括基本的 csv 文件。复杂计算
SQLite 采用 SQL 语句做计算,SQL 的优点和缺点都会继承下来。SQL 接近自然语言,学习门槛低,容易实现简单的计算,但不擅长复杂的计算,经常会造成代码会繁琐难懂。即使一些不太复杂的计算,SQL 实现起来也不容易。比如,计算每个客户销售额最大的 3 笔订单:- select * from (select *, row_number() over (partition by Client order by Amount desc) as row_number from Orders) where row_number<=3
复制代码 SQL 很难直接表达连续上涨的概念,只能换个方法变相实现,即通过累计不涨天数来计算连续上涨天数,这种方法技巧性强,编写难度大且不易理解。而且 SQL 难以调试,导致维护困难。
再看个例子:找出销售额占到一半的前 n 个客户,并按销售额从大到小排序。- 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 很难处理恰好要过线的客户,只能换个方法变相实现,即计算销售额从小到大的累计值,反过来找出累计值不在后一半的客户。这种方法技巧性强,代码冗长,而且难以调试。
除此之外,SQLite 的日期和字符串函数也不够丰富,比如缺乏季度增减、工作日计算等,这些缺点限制了 SQLite,不适合计算需求较复杂的场景。流程处理
SQL 本身缺乏流程处理能力,数据库会借助存储过程实现完整的业务逻辑,但 SQLite 不支持存储过程,也就无法直接实现完整的业务逻辑,只能借助主应用的能力,将 SQL 数据对象转为应用中的数据对象(比如 Java 的 resultSet/List 等对象),再用主程序的 for/if 等语句处理流程,最后再转回 SQL 的数据对象,代码非常繁琐。复杂的业务逻辑要在 SQL 对象和主应用的对象之间转换多次,更加麻烦,这里就不展示了。esProc SPL 解决 SQLite 困难如果要为 Java 小微型应用提供数据处理和计算能力,还有一个更好的选择:esProc SPL。esProc SPL 一个开源的数据处理引擎,架构简单,集成方便,可持久化存储数据,有足够的计算能力,这些特点与 SQLite 类似。SPL 架构简单,不用配置服务、节点、集群,只要引入 SPL 的 Jar 包,就可以部署在 Java 环境中。SPL 提供了 JDBC 接口,可以方便地集成到 Java 应用中,简单的查询类似 SQL。GitHub:https://github.com/SPLWare/esProc - 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
复制代码 SPL 支持数据持久化,可以将数据保存到自有数据格式(集文件)中,比如批量新增记录:
| A | 1 | =create(OrderID,Client,SellerID,Amount,OrderDate) | 2 | =A1.record([201,"HDR",9,2100.0,date("2021-01-01"),
202,"IBM",9,1900,date("2021-01-02"),
203,"APPLE",4,1900,date("2021-01-03")])
| 3 | =file("d:/Orders.btx").export@ab(A2) | 上面 A3 代码 export@ab,@a 表示追加,@b 表示集文件格式除了直接持久化,也可以先处理内存中的序表(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 语法,比如分组汇总:
[code]$select year(OrderDate) y,month(OrderDate) m, sum(Amount) s,count(1) c from {Orders} Where Amount>=? and Amount |