Java 有没有能在 txt/csv 上执行 SQL 的开源类库?

 

通常我们总是把文本数据加载到数据库中,然后再使用 SQL,但如果应用程序只使用一次数据,然后将其丢弃,这种情况加载到数据库就没有必要,因为需要先建表然后导入数据,过程繁琐,众多临时表既浪费存储,管理也很麻烦。如果只是单表查询,有些数据库支持文本表,即应用程序可以通过数据库内部查询外部文件中的数据,比如,h2 和 HSQLDB 可以执行,SELECT * FROM CSVREAD(‘test.csv’); ,但如果想和其他表做关联查询,还是需要将 test.csv 导成正常的内部表。

还有一个办法是用 Java 计算包,直接在 Java 中用 SQL,比如,CsvJdbc 和 Open-esProc。CsvJdbc比较简单,数据必须是 csv 格式或比较规整的 txt 文件,具体用法就是通过 CsvJdbc 驱动,对指定文件执行 SQL。Open-esProc则更强大一些,首先 Open-esProc 不是像 CsvJdbc 单一为了支持在 txt/csv 上执行 SQL 的计算包,而是全方位专业的处理结构化数据为目标的计算包。和普通 jar 包用法也不同,它是将数据类型和方法封装到一个叫 SPL 的脚本语言中,通过先 SPL 计算数据,然后在 Java 程序中调用 SPL 脚本,返回 ResultSet 对象,使用方式其实和 CsvJdbc 相同,只不过 SPL 中将完善的用 SQL 查询文件数据的方法封装起来了。

Open-esProc 常规用法是先在专门的 SPL IDE 中书写 SQL 语句,将脚本单独保存,然后在 Java 程序中调用。比如:计算学生成绩表中全体学生的语文平均分、数学最高分、英语总分。SPL 脚本如下:


A

1

$select   avg(Chinese),max(Math),sum(English) from E:/txt/Students_scores.txt

脚本文件(比如 condition.dfx)和 Java 存储在一起,通过 JDBC 接口在 JAVA 中调用,用法类似存储过程。

…
 ResultSet result = statement.executeQuery("call condition.dfx");
…

SPL 也支持类似 SQL 的用法,无须脚本文件,直接将其嵌入 JAVA

…
 ResultSet result = statement.executeQuery("
$select avg(Chinese),max(Math),sum(English) from E:/txt/Students_scores.txt");
…

SPL 支持 SQL92 标准中的大部分语法,比如使用 SQL 的 with 子句对文本文件中的数据进行计算。
示例:从部门数据文件中找出指定部门HRR&DSales,再计算这几个部门女员工人数和平均工资。


A

1

$with A   as
(select   NAME as DEPT from E:/txt/DEPARTMENT.txt
where   NAME='HR' or NAME='R&D' or NAME='Sales')
 
select   A.DEPT DEPT,count(*) NUM,avg(B.SALARY) AVG_SALARY from
A left   join E:/txt/EMPLOYEE.txt B
on   A.DEPT=B.DEPT
where   B.GENDER='F' group by A.DEPT

了解更多 SPL 中的 SQL 示例,可以参考 在文件上使用 SQL 查询的示例

Open-esProc 相比 CsvJdbc,还能很方便支持格式不规则的文本文件,比如,无列名的情况


A

1

$select *   from {file("sOrderNT.txt").import()} where _2='TAS'

从 SQL 中可以看到,数据无列名时,则以 "下划线 + 序号" 为默认列名,第 2 列的列名就用 _2 表示,其他列以此类推。{} 内是 SPL 语言,其中 import 函数可以读取格式更复杂的文件。

为了更直观地使用列名,可以先改列名再计算。传统的 SQL 写法是用子查询改列名,上面的 SQL 可改为:


A

1

$select * from (  select \_1 OrderID ,\_2   Client, \_3 SellerId, \_4 Amount, _5 OrderDate from   {file("sOrderNT.txt").import()})

有些文件的分隔符不是常见的制表符或逗号,而是分号冒号等特殊符号,有时甚至用多个字符当分隔符,这种情况下应该用 SPL 语句中的 import 函数来指定分隔符。SPL 对特殊分隔符,也能很灵活的支持,比如 sep.txt 的分隔符是双线 ||,则可以用下面的 SQL 来读取:


A

1

$select * from {file("sep.txt").import@t(;,"||")}

还有一些其他的怪异的情况,比如,有些文件没有用回车换行区分记录,而是用其他符号,这样的文件整体呈现为一个大字符串;有些文件存在空行,需要跳过才能使用等情况都能很好的支持,更多示例参考 esProc 的 SQL 应用方案