ORACLE 11G利用 ORDS+pljson来实现json_table 效果
Oracle 在12.1中引入了对json的支持,可以利用sql来查询json字段各个节点的值,对于11G的版本,例如EBS环境,不方便升级到12C,可以利用开源软件pljson 来实现,本文来利用ORDS + pljson 来实现开发一个post示例请求,接收订单数据。
涉及软件组件
ORDS 20.2
pljson 3.5.2
Oracle dbms 11GR2
Oracle apex 20.1
json数据源实例
下面这一段就是要处理的json,是多层次的机构,包含订单头信息和两个订单行
{"PONumber": 1608,"Requestor": "Alexis Bull","CostCenter": "A50","Address": {"street": "200 Sporting Green","city": "South San Francisco","state": "CA","zipCode": 99236,"country": "United States of America"},"LineItems": [{"ItemNumber": 1,"Part": {"Description": "One Magic Christmas","UnitPrice": 19.95,"UPCCode": 1313109289},"Quantity": 9.0},{"ItemNumber": 2,"Part": {"Description": "Lethal Weapon","UnitPrice": 19.95,"UPCCode": 8539162892},"Quantity": 5.0}]
}
数据来源参考文档
技术实现
在上面的Oracle官方文档中,使用了json_table函数,这个函数和11G的xmltable函数语法风格很像,这个是12C之后才有的一个新功能,对于处理json字符串十分方便,但是对于12.1之前的版本没这个功能,Oracle总是慢一步,在12C之前大家对json需求比较强烈,于是就有了开源工具pljson,这个插件本质上对Oracle集合的一些运用。
但是开源的不如原生的从底层实现的好用,比如有个查询功能如下面这段代码:
select *
from table(
pljson_table.json_table(
'{"data":{"name": "name 3","description": "blah blah...","type": "text","created_time": "2015-12-21T19:23:29+0000","shares": { "count": 100 },"extra": null,"maps" : [ true, true, false ]}
}',
pljson_varray('data.name', 'data.extra', 'data.maps', 'data.shares.count', 'data.missing'),
pljson_varray('name', 'extra', 'map', 'count', 'whatelse'))
)
作为一个sql脚本在sql引擎中运行时没问题的,但是在pl/sql引擎下是会报错 ORA-22905 无法从非嵌套表项访问行,本质是用table的时候需要在pl/sql块中声明下table里面的是个啥类型。但是pljson_table.json_table的返回值是一个anydataset,这个Oracle ANYDATASET type又有一套玩法,暂时不去研究怎么实现在pl/sql里面用这个了。转变下思路,我们可以在上面这个查询封装到一个视图里面,视图是用的sql引擎,然后在pl/sql里面对这个视图进行查询就好了。
官方代码
我们本质上是要实现如下效果,下面代码来自Oracle官方ORDS 文档。
DeclareL_PO BLOB;BeginL_PO := :body;INSERT INTO PurchaseOrderSELECT * FROM json_table(L_PO FORMAT JSON, '$'COLUMNS (PONo Number PATH '$.PONumber',Requestor VARCHAR2 PATH '$.Requestor',CostCenter VARCHAR2 PATH '$.CostCenter',AddressStreet VARCHAR2 PATH '$.Address.street',AddressCity VARCHAR2 PATH '$.Address.city',AddressState VARCHAR2 PATH '$.Address.state',AddressZip VARCHAR2 PATH '$.Address.zipCode',AddressCountry VARCHAR2 PATH '$.Address.country'));INSERT INTO LineItem
SELECT * FROM json_table(L_PO FORMAT JSON, '$'COLUMNS (PONo Number PATH '$.PONumber',NESTED PATH '$.LineItems[*]'COLUMNS (ItemNumber Number PATH '$.ItemNumber',PartDescription VARCHAR2 PATH '$.Part.Description',PartUnitPrice Number PATH '$.Part.UnitPrice',PartUPCCode Number PATH '$.Part.UPCCode',Quantity Number PATH '$.Quantity')));
commit;
end;
json_table(L_PO FORMAT JSON 这个功能很强大,一下子实现了很多东西,blob 到clob的转换,集合的定义。
为此我们需要自己实现blob到clob转换,因为pljson接收的入参必须是clob类型。
表结构
表结构如下:
--接收POST过来原始数据的表
-- Create table
create table CUX_APEX_JSON_CLOB_DATA
(request_id NUMBER not null,json_lob CLOB,creation_date DATE default SYSDATE
);
-- Create/Recreate indexes
create unique index CUX_APEX_JSON_CLOB_DATA_U1 on CUX_APEX_JSON_CLOB_DATA (REQUEST_ID);
--序列
create sequence CUX_APEX_JSON_CLOB_DATA_S;-- Create table 订单头表
create table PURCHASEORDER
(pono NUMBER(5) not null,requestor VARCHAR2(50),costcenter VARCHAR2(5),addressstreet VARCHAR2(50),addresscity VARCHAR2(50),addressstate VARCHAR2(2),addresszip VARCHAR2(10),addresscountry VARCHAR2(50),request_id NUMBER not null
);
-- Create/Recreate primary, unique and foreign key constraints
alter table PURCHASEORDERadd primary key (PONO);-- Create table 订单行表
create table LINEITEM
(pono NUMBER(5) not null,itemnumber NUMBER(10) not null,partdescription VARCHAR2(50),partunitprice NUMBER(10),partupccode NUMBER(10),quantity NUMBER(10),qequest_id NUMBER not null
);
-- Create/Recreate primary, unique and foreign key constraints
alter table LINEITEMadd primary key (PONO, ITEMNUMBER);
bloc_to_clob转换函数
CREATE OR REPLACE FUNCTION blob_to_clob(blob_in IN BLOB) RETURN CLOB ASv_clob CLOB;v_varchar VARCHAR2(32767);v_start PLS_INTEGER := 1;v_buffer PLS_INTEGER := 32767;
BEGINDBMS_LOB.CREATETEMPORARY(v_clob, TRUE);FOR i IN 1 .. CEIL(DBMS_LOB.GETLENGTH(blob_in) / v_buffer) LOOPv_varchar := UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(blob_in,v_buffer,v_start));DBMS_LOB.WRITEAPPEND(v_clob, LENGTH(v_varchar), v_varchar);--DBMS_OUTPUT.PUT_LINE(v_varchar);v_start := v_start + v_buffer;END LOOP;RETURN v_clob;
END blob_to_clob;
视图
--------------------需要注意,视图select字段必须大写----------------------------------
--头表视图
CREATE OR REPLACE VIEW DEMO_HEADER_V AS
SELECT js."PONO",js."REQUESTOR",js."COSTCENTER",js."ADDRESSSTREET",js."ADDRESSCITY",js."ADDRESSSTATE",js."ADDRESSZIP",js."ADDRESSCOUNTRY",tab.request_idFROM cux_apex_json_clob_data tab,TABLE(pljson_table.json_table(tab.json_lob,pljson_varray('PONumber','Requestor','CostCenter','Address.street','Address.city','Address.state','Address.zipCode','Address.country'),pljson_varray('pono','requestor','costcenter','addressstreet','addresscity','addressstate','addresszip','addresscountry'))) js;--行表视图
CREATE OR REPLACE VIEW DEMO_LINE_V AS
SELECT js."PONO",js."ITEMNUMBER",js."PARTDESCRIPTION",js."PARTUNITPRICE",js."PARTUPCCODE",js."QUANTITY",pljtt.request_idFROM cux_apex_json_clob_data pljtt,TABLE(pljson_table.json_table(pljtt.json_lob,pljson_varray('PONumber','LineItems[*].ItemNumber','LineItems[*].Part.Description','LineItems[*].Part.UnitPrice','LineItems[*].Part.UPCCode','LineItems[*].Quantity'),pljson_varray('pono','itemnumber','partdescription','partunitprice','partupccode','quantity'),table_mode => 'nested')) js;
pl/sql 代码
CREATE OR REPLACE PACKAGE cux_apex_json_demo_pkg ISPROCEDURE create_order(p_body IN BLOB);END cux_apex_json_demo_pkg;
/
CREATE OR REPLACE PACKAGE BODY cux_apex_json_demo_pkg IS--******************************************************************************-- FUNCTION get_tax_rate---- p_body POST请求过来的BLOB数据---- Public. demo演示,获取POST过来的json数据,将其入库----******************************************************************************PROCEDURE create_order(p_body IN BLOB)ISc_body CLOB := blob_to_clob(p_body);l_request_id NUMBER := cux_apex_json_clob_data_s.nextval;BEGININSERT INTO cux_apex_json_clob_dataVALUES(l_request_id, c_body, SYSDATE);plog.debug(l_request_id || '-insert cux_apex_json_clob_data rows:' || SQL%ROWCOUNT);INSERT INTO PurchaseOrderSELECT *FROM demo_header_vWHERE request_id = l_request_id;plog.debug(l_request_id || '-insert PurchaseOrder rows:' || SQL%ROWCOUNT);INSERT INTO LineItemSELECT *FROM demo_line_vWHERE request_id = l_request_id;plog.debug(l_request_id || '-insert LineItem rows:' || SQL%ROWCOUNT);COMMIT;EXCEPTIONWHEN OTHERS THENplog.full_error_backtrace;RAISE;END create_order;BEGINNULL;
END cux_apex_json_demo_pkg;
/
封装到ORDS中
BEGINORDS.ENABLE_SCHEMA(p_enabled => TRUE,p_schema => 'CUX_APEX',p_url_mapping_type => 'BASE_PATH',p_url_mapping_pattern => 'CUX_APEX',p_auto_rest_auth => FALSE);ORDS.DEFINE_MODULE(p_module_name => 'demo',p_base_path => '/demo/',p_items_per_page => 25,p_status => 'PUBLISHED',p_comments => 'json多行测试');ORDS.DEFINE_TEMPLATE(p_module_name => 'demo',p_pattern => 'test',p_priority => 0,p_etag_type => 'HASH',p_etag_query => NULL,p_comments => NULL);ORDS.DEFINE_HANDLER(p_module_name => 'demo',p_pattern => 'test',p_method => 'POST',p_source_type => 'plsql/block',p_items_per_page => 0,p_mimes_allowed => '',p_comments => NULL,p_source =>
'BEGINcux_apex_json_demo_pkg.create_order(:BODY);
END;
');COMMIT;END;
在aepx中的定义是如下效果:
附加更新
上面的例子需要将json数据入库,其实用动态sql的话,可以将json作为一个绑定变量传入,这样就避免了插入冗余数据了,但是会丢失原始传过来的json数据。
--json内容{"DATA":
[{"storeId":"1","supplierId":"111","takeCost":"1.00","takeDate":"20191112"},
{"storeId":"2","supplierId":"2","takeCost":"200.00","takeDate":"20191114"},
{"storeId":"2","supplierId":"3","takeCost":"100.00","takeDate":"20191111"}]
}--动态sql
execute immediate q'{ insert into supp_cost_t(storeid,supplierid,takecost,takedate)select b.storeid, b.supplierId, b.takeCost, b.takeDatefrom table(pljson_table.json_table(:1,pljson_varray('DATA[*].storeId','DATA[*].supplierId','DATA[*].takeCost','DATA[*].takeDate'),pljson_varray('storeId','supplierId','takeCost','takeDate'),table_mode => 'nested')) b}'using json_CLOB;
参考博客:
https://darkathena.blog.csdn.net/article/details/120644329?spm=1001.2014.3001.5502
ORACLE 11G利用 ORDS+pljson来实现json_table 效果相关推荐
- Oracle备份standby,Oracle 11g 利用泠备份恢复standby库
Oracle 11g 利用泠备份恢复standby库 1 开始在备库上进行泠备份 先查好控制文件.redo.undo文件.数据文件的路径 1.1 先关闭主库的归档日志传输 SQL> ALTER ...
- 利用sqoop将oracle 11g中的表迁移至hive表
[Prerequisite] hadoop, hive, sqoop installed ojdbc6.jar was in directory /usr/lib/sqoop/lib (ojdbc6. ...
- oracle utl_smtp,Oracle 11g 环境下,利用utl_smtp创建发送邮件的存储过程
网上太多发邮件储存过程,我就不转发了,弄个简单的作为示例: create or replace procedure Send_mail(mail_body varchar2) is smtp_conn ...
- Oracle 11G for redhat 自启动脚本
Oracle 11G for redhat 自启动脚本 在$ORACLE_HOME/bin中,有dbstart和dbshut这两个脚本,可以使用这两个oracle自带的脚本实现oracle的开机自启动 ...
- Oracle 11g AMM与ASMM切换
现在的Oracle正在往智能化方向发展.如果我们现在找一些8i/9i时代的Oracle书籍,怎么样配置合适的数据库各内存池大小是非常重要的话题.但是进入10g之后,自动内存池调节成为一个重要Oracl ...
- Oracle 11g数据库的部署
一.前言 最近要准备考试了,准备温习下Oracle数据库的相关知识,那么学习Oracle的前提肯定是部署了,目前主流Oracle版本为11g,那么接下来我们就先来一起看下,如何部署Oracle已经对数 ...
- Oracle 11g 新特性简介
Oracle 11g于2007年7月11日美国东部时间11时(北京时间11日22时)正式发布,11g是甲骨文公司30年来发布的最重要的数据库版本,根据用户的需求实现了信息生命周期管理(Informat ...
- Oracle 11g RAC features
<一,> oracle 11g r2 RAC提供了以下功能: 高可用:shared-everything 模式保证了单节点的故障不会停止服务,集群中的其他节点将快速接管 可扩展性:多节点分 ...
- oracle查询100到200数据,100分数据库查询语句(ORACLE 11g)
100分求一个数据库查询语句(ORACLE 11g) 一张表tabl1 如下: epqname createtime e ...
- t oracle删除吗,Oracle 11g 手工建库与删库
Oracle 11g 手工建库与删库 在前一篇文章中提到阅读Oracle 11gR2 Administrator's Guide(文档编号E25494-01)时,简单描述了DDL_LOCK_TIMEO ...
最新文章
- pandas使用groupby.last函数获取每个组中的最后一个值实战:groupby.last函数获取每个组中的最后一个值、groupby.nth函数获取每个组中的最后一个值
- 人脸识别之insightface开源代码使用:训练、验证、测试(4)
- 深度学习小技巧(二):如何保存和恢复scikit-learn训练的模型
- 取消挂载点可以节省磁盘么_Linux下NTFS格式外接硬盘的挂载,取消挂载和开机自动挂载...
- springboot 系列教程十:springboot单元测试
- dom对象常用的属性和方法有哪些?
- 来自百度的一篇如何关闭Centos7的防火墙以及如何永久关闭防火墙的指令教学【转载】
- 微信商户现金红包api php
- linux蚂蚁矿池教程,蚂蚁矿机Z9使用教程
- HTML+CSS小白入门与进阶教程
- OCP 11G 053题库解析汇总链接(1-200)
- 模糊特征隶属度及图像增强相关
- 阿里云ECS开放指定端口步骤
- 算力智库2021隐私计算论坛圆满落幕,隐私计算落地会长出怎样的新商业模式?
- Tableau 5阶桑基图
- 锁模块之当前读和快照读
- 软件测试之自动化测试Pytest教程
- html如何制作一个漂亮的表格?+ 列表制作 + 表单制作(干货!直接收代码)
- 亚马逊A9算法是什么?有什么作用?(案例详解)
- Windows远程桌面(xrdp+vnc)连接Ubu…