SPL:跨数据库移植 SQL
对于不同类型的数据库,即使是数据库都能支持的SQL查询,语法也大体相似,但仍然可能会有细微差别而不能通用。主要原因在于这些数据库各自的函数参数规则不同。
比如将字符串"2020-02-05"转换成日期, 不同数据库有不同的写法:
ORACLE:select TO_DATE('2020-02-05', 'YYYY-MM-DD') from USER;
SQLServer:select CONVERT(varchar(100), '2020-02-05', 23) from USER;
MYSQL:select DATE_FORMAT('2020-02-05','%Y-%m-%d') from USER;
如果希望应用在不同的数据库之间切换,就需要改写SQL语句,很麻烦。
SPL提供了sqltranslate函数,可以自动将SPL中简单SQL语句的函数翻译成各数据库的写法,这样就方便多了。
1. sqltranslate函数用法
sql.sqltranslate(dbtype)
这个函数可以将SPL中简单SQL中的函数翻译成各种不同数据库的写法。这里的sql是需要翻译的SQL语句,dbtype是数据库类型。函数是指在SPL的简单SQL中定义过的函数,未定义的不会被翻译。已定义的函数列表和数据库类型请查阅sqltranslate函数帮助
编写一个SPL脚本文件sqltrans.dfx,设置两个参数sql和dbtype:
A |
注释 |
|
1 |
=sql.sqltranslate(dbtype) |
将sql翻译成符合dbtype语法的SQL语句 |
2 |
=connect(dbtype) |
连接数据库 |
3 |
=A2.query@x(A1) |
用翻译后的A1在数据库查询出结果,关闭数据库连接 |
假设sql为下面的语句:
SELECT EID, NAME, BIRTHDAY, ADDDAYS(BIRTHDAY,10) DAY10 FROM EMP
运行这个脚本时传入参数dbtype为ORACLE,运行后A1结果如下:
SELECT EID, NAME, BIRTHDAY, BIRTHDAY+NUMTODSINTERVAL(10,'DAY') DAY10 FROM EMP
如果传入参数dbtype为SQLSVR(即SQL SERVER),运行后A1结果如下:
SELECT EID, NAME, BIRTHDAY, DATEADD(DD,10,BIRTHDAY) DAY10 FROM EMP
2. 在Java中调用
SPL可以用JDBC方式被Java调用,调用sqltrans.dfx的代码如下:
Class.forName("com.esproc.jdbc.InternalDriver");
Connection con = DriverManager.getConnection("jdbc:esproc:local://");
PreparedStatement st = con.prepareCall("call sqltrans(?,?)");
st.setObject(1, "SELECT EID, NAME, BIRTHDAY, ADDDAYS(BIRTHDAY,10) DAY10 FROM EMP");
st.setObject(2, "ORACLE");
ResultSet rs = st.executeQuery();
这里的rs就是脚本文件中A3格返回的查询结果集。
更深入用法可参考《Java如何调用SPL脚本》
如果只需要在Java程序中翻译SQL语句,可以直接调用api方法:
String sql = "SELECT NAME, BIRTHDAY, ADDDAYS(BIRTHDAY,10) DAY10 FROM EMP";
sql = com.scudata.dm.sql.SQLUtil.translate(sql, "ORACLE");
3. 新增数据库和函数定义
SPL中简单SQL的数据库类型和函数是在发布包esproc-bin-xxxx.jar中的字典文件/com/scudata/dm/sql/function.xml中定义的。文件部分内容如下图:
文件中FUNCTIONS节点代表一个函数组,type是函数组类型,FixParam表示是参数个数固定的函数组。
FUNCTION节点代表一个简单SQL函数,name是函数名,paramcount是参数个数,value是翻译本函数时的默认值,空串时表示无需翻译。
INFO节点代表一种数据库,dbtype是数据库名称,空串时表示是SPL中的简单SQL,value是翻译到本数据库时的对应值。value中的?或?1代表函数的第1个参数值,?2代表函数的第2个参数值……依此类推。当INFO中的value值为空串时,则使用父节点FUNCTION的value值。
在翻译时,如果FUNCTION节点下没有指定数据库的INFO节点定义,则此函数保持原样,不被翻译。
如果要翻译的目标数据库在字典文件中没有定义,怎么办?可以修改字典文件来增加目标数据库,在需要翻译的函数节点FUNCTION下增加一个INFO节点。比如目标数据库名为MYDB,此数据库中对应函数ADDDAYS的语法为dayPlus(date,days),那么在字典中增加INFO节点如下:
修改字典后再运行sqltranslate("MYDB"),则前例中的SQL语句会被翻译成:
SELECT EID, NAME, BIRTHDAY, dayPlus(BIRTHDAY,10) DAY10 FROM EMP
如果需要增加一个新的函数,则在字典文件中新增写一个FUNCTION节点,再给它增加一些INFO子节点定义各种数据库中此函数应被翻译成什么语法即可。