文本参考文档:
https://help.sap.com/docs/HANA_SERVICE_CF/7c78579ce9b14a669c1f3295b0d8ca16/f8f6916b5c434a6fbd1bb7e0dc65acd4.html?locale=en-US
升级HANA数据库后,想必不少项目保存接口日志,是用STRING字符串来保存吧。过去为了保存一个未知长度的字符串,还是挺费劲的,并且不利于查询。现在用了STRING字符串来保存,相比一定方便了不少吧。
好吧,我承认,很长一段时间STRING唯一的优点就是直接明文保存字符串,模糊查询的时候确实挺方便,如果你不是很在乎效率的话。但有的时候,我们需要查询历史上的一些东西,比如翻出来某个未知时间段的凭证,这个时候模糊查询就不是很好用了。已经是HANA了,一定会有一个更加轻松的方式来解析JSON了吧?
是的,我的朋友,是的!HANA SQL Script了解一下!
虽然ABAP的SQL依旧贫弱,但只要放弃我们的老朋友,就可以变强。HANA的SQL Script有一个叫JSON_TABLE的语法,它可以帮助你将JSON解析成合适的数组进行输出。我们现在搭建一个例子。
日志表:

简化的日志表,name假设是区分接口,json就是传入的json字符串。
解析保存表:


定义一个表类型和结构,后面会用到。
然后写个程序保存一个JSON

{ "item": [ { "ebeln": 1, "ekpo": [ { "ebelp": 10 }, { "ebelp": 20 } ] }, { "ebeln": 2, "ekpo": [ { "ebelp": 30 }, { "ebelp": 40 } ] } ] } |
好,准备工作完成,我们需要怎么解析这个JSON呢?
因为我们的日志解析程序大多是ABAP环境下,所以这里需要用AMDP的方式去运行HANA SQL Script。AMDP的类开发不能用SE24,需要在Esclipse上进行(必须吐槽下SAP,我真没觉得在SE24上开发这个类和在Esclipse上有啥区别)。
下面是类代码:
CLASSztest_amdp DEFINITION PUBLIC FINAL CREATE PUBLIC .
PUBLIC SECTION. INTERFACES if_amdp_marker_hdb. CLASS-DATA: lt_ekko TYPE zty_t_ekpo. METHODS select_ztlog IMPORTING VALUE(clnt) TYPE mandt EXPORTING VALUE(lt_ekko) TYPE zty_t_ekpo. ENDCLASS.
CLASS ztest_amdp IMPLEMENTATION. METHOD select_ztlog BY DATABASE PROCEDURE FOR HDB LANGUAGE SQLSCRIPT OPTIONS READ-ONLY USING ztlog.
lt_ekko = select * FROM json_table( ( SELECT json FROM ZTLOG WHERE mandt = clnt AND name = 1 ORDER BY name ),'$.item[*]' COLUMNS ( EBELN nvarCHAR( 10 ) PATH'$.ebeln', NESTEDPATH'$.ekpo[*]' COLUMNS ( EBELP nvarCHAR( 5 ) PATH'$.ebelp' ) ) ) as lt_ekko where ebeln = 2;
ENDMETHOD. ENDCLASS.
|
首先我们的目标是将JSON中,ebeln为2的订单取出来,放在表里。已知订单都在item这个表中,订单行ebelp是个嵌套数组,那么我们要怎么取数,将订单号和订单行放在一个表里面呢?
首先是划定下最基础的取数范围,我们要取ztlog表name为1,client号是001的数据,然后指定json这个表字段。其实client号取决于实际你存放数据的客户端号。

这里我们变形下方便理解。

如果不需要对ztlog过滤,这里可以直接指向ztlog的json字段,方法就是<表.字段>。逗号后面的'$.item[*]',表示要取json结构中item这个数组。
这里推荐阅读官方文档配合理解
Description
Queries a JSON text and presents it as a relational table.
The following tokens are supported in
Token |
Description |
Example |
$ |
The current context item. |
'$' |
. |
The member of an object. |
'$.item.description' |
[ |
The array index specifier (open). |
|
] |
The array index specifier (closed). |
'$[1]' '$.item.list[1]' |
to |
The array index range. |
'$[3 to 5]' = '$[3,4,5]' |
* |
The wild card. |
'$.*.description' '$.item.list[*]' |

这一段就是取我们的json内容了。参考json格式,ebeln这个字段是在item.ebeln这个字段上,ebelp在item.ekpo[*]这个数组里,因此,获取EBELN的值时,只需要简单的将EBELN的值,指定格式,指定路径取出即可。但是EBELP,就需要用NESTED进行嵌套字段的读取。
NESTED PATH可以多次使用,这样可以将多个表或结构放入到一个解析表中。不过需要注意两点,一是层级。例如本次案例,我们取的都是json中item下的数据,NESTED PATH不能取到item外面的数据。其次就是如果你打算NESTED PATH平级的多个个表,数据量最后会爆炸的,使用的时候需要想清楚你要做什么。比较建议的做法就是NESTED PATH多个平级的结构,一个平级的数组。
最后的AS别名不是必须的。

最后的where是对解析出来的json值进行过滤。这里只取ebeln为2的值。
这里的转换类型,参考官方文档
Specifies the data type.
BIGINT
| DATE
| DECIMAL
| DOUBLE
| INT
| NVARCHAR (
| SECONDDATE
| SMALLDECIMAL
| TIME
| TIMESTAMP
| VARCHAR (
这里EBELN和EBELP都是NUMC的类型字段,所以转换格式时要用NVARCHAR,并指定长度
我们看下运行效果:



可以看到过滤效果非常完美。经过测试,如果不限定ztlog.name的取值,可以成功将ebeln=2的30,40,50,60行都取到LT_EKKO中。
需要注意的是,这里的返回表限制为扁平表。也就是说,你可以将嵌套的json解析为不嵌套的表,但是不能将嵌套结构本身解析出来。

