怎么轻松实现报表跨库移植
与一般事务处理(TP)系统相比,报表中的 SQL 更频繁地使用各种计算函数,计算逻辑也更加复杂,对 SQL 的依赖更强。报表开发中可能要应对数据库的变化,不同数据库的 SQL 语法虽大体相似,但在细节上还会存在差异,这要修改报表的 SQL 才能适应不同数据库。这种工作既耗费大量人力又容易引发错误。
完全自动改造 SQL 是不现实的,因为数据库功能的差异使得某些复杂计算很难直接迁移。不过,梳理一下会发现,大部分不兼容问题都是由于 SQL 函数写法不同造成的。特别是日期和字符串相关的函数,业界没有标准,各个数据库各行其是。
比如将字符串 “2020-02-05” 转换成日期,不同数据库有不同的写法。
#ORACLE:
select TO_DATE('2020-02-05', 'YYYY-MM-DD') from USER
#SQL Server:
select CONVERT(varchar(100), '2020-02-05', 23) from USER
#MySQL:
select DATE_FORMAT('2020-02-05','%Y-%m-%d') from USER
用了这类 SQL 的报表在不同的数据库之间切换时,就需要改写 SQL 语句。
怎么做到换库后简单快速地实现报表移植呢?借助 SPL 可以轻松搞定。
SPL 针对跨库移植 SQL 的场景提供了 SQL 转换功能,可以将 SPL 定义过的标准 SQL 转换成不同数据库对应的语句,从而实现报表在各个数据库之间移植,更换数据库也不必修改报表。
这里以润乾报表为例简单说明实施过程,其他报表也是类似的。因为 SPL 可以嵌入到报表应用中使用,而且封装了标准 JDBC,所以引入后的架构与原来变化不大。
报表模板中使用的标准 SQL,其 SQL 通过 JDBC 发送给 SPL 转换成对应数据库的 SQL 语法执行。更换数据库时只需要重新配置数据源连接,报表模板完全不用动,几乎可以做到透明移植报表。
比如这里的订单查询报表,其中 SQL 中使用了 adddays 函数:
SELECT order_id,user_id,order_date,adddays(order_date,10) as day10,total_amount FROM orders where order_date>=? and order_date<=?
这个 adddays 函数不同数据库的写法是不一样的:
Oracle 是:order_date+NUMTODSINTERVAL(10,'DAY')
MySQL 是:order_date+INTERVAL 10 DAY
PostgreSQL 是:order_date+interval '10 days'
MSSQL 又是这样:DATEADD(DD,10,order_date)
如果数据库配置的是 MySQL,怎么能转化成 MySQL 的语法呢?
需要先配置一个 SPL 数据源,标准 JDBC 配置方式:
然后在连接这个 JDBC 的 URL 中增加这样一串参数:jobVars=dbType:MYSQL,dbName:mysql,dbType 代表数据库类型,dbName 则代表数据源名称。这两个参数是做什么的呢?先往后看。
接着需要将报表模板的数据源指定成 esproc:
这么配置完,报表运行时就会将 SQL 发给 SPL。然后就是要完成翻译的工作了。
SPL 提供了 JDBC 网关功能,所有通过 JDBC 发送的 SQL 都会交给网关处理,我们自然可以在网关中完成翻译和数据查询。
把 SPL 的核心配置文件 raqsoftConfig.xml 放在应用类目录后配置一下网关:
<JDBC><load>Runtime,Server</load><gateway>gateway.splx</gateway>
</JDBC>
这里指定了一个 SPL 脚本,下面是脚本内容:
A | B | |
1 | =sql=trim(sql).sqltranslate(dbType) | |
2 | =argsN=args.len() | =("sql"|argsN.("args("/~/")")).concat@c() |
3 | =connect(dbName) | return A3.query@x(${B2}) |
这是一个通用脚本,可以处理任意 SQL。脚本有两个参数,sql 用于接收报表传递的 SQL 语句,args 接收 SQL 中的参数:
而脚本还用到了 dbType 和 dbName,这就是 JDBC 中 url 传递过来的两个参数。SPL 的 SQL 翻译函数 sqltranslate 需要类型参数,最后执行 SQL 查询数据也需要数据源信息(dbName)。
这个数据源连接是在 SPL 配置文件 raqsoftConfig.xml 中设置的:
<DB name="mysql">
<property name="url" value="jdbc:mysql://127.0.0.1:3306/raqdb?useSSL=false&useCursorFetch=true"></property>
<property name="driver" value="com.mysql.jdbc.Driver"></property>
…
</DB>
也就是 MySQL 的数据库连接,更换数据库需要重新配置数据源,并将报表中的 esproc 数据源 URL 也对应修改即可。
完成上面的步骤,再查询报表就可以正常展现:
监控后台的 SQL 也可以发现原来的:
SELECT order_id,user_id,order_date,adddays(order_date,10) as day10,total_amount FROM orders where order_date>=? and order_date<=?
已经被翻译成 MySQL 对应的语法:
SELECT order_id,user_id,order_date,order_date+INTERVAL 10 DAY as day10,total_amount FROM orders where order_date>=? and order_date<=?
如果要换数据库怎么办?只要改 SPL JDBC 中的 URL 参数和数据库数据源配置。比如要换成 PG,URL 改成这样:
url=”jdbc:esproc:local://?jobVars=dbType: POSTGRES,dbName:pgds”
对应的数据源改成:
<DB name="pgds">
<property name="url" value="jdbc:postgresql://192.168.0.1:5432/mypg"></property>
<property name="driver" value="org.postgresql.Driver"></property>
…
</DB>
这些函数和数据库的对应关系,如何扩展函数列表,以及一些不固定参数的函数如何处理,SPL 都提供了接口,简单配置就能完成,具体可以参考论坛资料。
最后总结一下 SPL 实现跨库移植报表的过程。
1. 集成 SPL
2. 配置 SPL 数据源,让报表都连这个
3. 配置 SPL 网关,并编写网关脚本(上面的直接拿来用)
基本上就这三步就完事了,以后换库就把数据源连接改一下就 OK 了,报表完全不用动,跨库轻松移植报表可不是说说而已。
而且用润乾报表还能简单一些,因为已经内置了 SPL,不需要集成那步,简化到两步就能实现。作为国内主流、老牌的报表工具,现在只要极低成本就可以永久使用:一万一套,三万买断。