Scival REST API to database table for analysis - JSON Path - oracle

I'm trying to get some specific dataset from Scival REST API to oracle database table. Below is the JSON payload that I'm trying to manipulate.
{
"metrics": [{
"metricType": "ScholarlyOutput",
"valueByYear": {
"2017": 4,
"2018": 0,
"2019": 3,
"2020": 1,
"2021": 1
}
}],
"author": {
"link": {
"#ref": "self",
"#href": "https://api.elsevier.com/analytics/scival/author/123456789?apiKey=xxxxxxxxxx&httpAccept=text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2",
"#type": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
},
"name": "Citizen, John",
"id": 123456789,
"uri": "Author/123456789"
}
}
I'm able to query the 'author' bit with the below SQL.
SELECT jt.*
FROM TABLE d,
JSON_TABLE(d.column format json, '$.author' COLUMNS (
"id" VARCHAR2 PATH '$.id',
"name" VARCHAR2 PATH '$.name')
) jt;
However, I'm not able to get the 'valueByYear' value. I've tried below.
SELECT jt.*
FROM TABLE d,
JSON_TABLE
(d.column, '$.metrics[*]' COLUMNS
(
"metric_Type" VARCHAR2 PATH '$.metricType'
,"Value_By_Year" NUMBER PATH '$.valueByYear'
NESTED PATH '$.valueByYear[1]' COLUMNS
("2021" NUMBER PATH '$.valueByYear[1]'
)
)
) jt;
I would appreciate if you could let me know what I'm missing here. I'm after the latest 'year' value.

You can use:
SELECT jt.*
FROM table_name d,
JSON_TABLE(
d.column_name format json,
'$'
COLUMNS (
id VARCHAR2 PATH '$.author.id',
name VARCHAR2 PATH '$.author.name',
NESTED PATH '$.metrics[*]' COLUMNS (
metricType VARCHAR2(30) PATH '$.metricType',
value2021 NUMBER PATH '$.valueByYear."2021"'
)
)
) jt;
Which, for the sample data:
CREATE TABLE table_name (
column_name CLOB CHECK (column_name IS JSON)
);
INSERT INTO table_name (column_name) VALUES (
'{
"metrics": [{
"metricType": "ScholarlyOutput",
"valueByYear": {
"2017": 4,
"2018": 0,
"2019": 3,
"2020": 1,
"2021": 1
}
}],
"author": {
"link": {
"#ref": "self",
"#href": "https://api.elsevier.com/analytics/scival/author/123456789?apiKey=xxxxxxxxxx&httpAccept=text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2",
"#type": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
},
"name": "Citizen, John",
"id": 123456789,
"uri": "Author/123456789"
}
}'
);
Outputs:
ID
NAME
METRICTYPE
VALUE2021
123456789
Citizen, John
ScholarlyOutput
1
db<>fiddle here

If you want to do it dynamically in PL/SQL, then you can create the types:
CREATE TYPE scival_row IS OBJECT(
name VARCHAR2(100),
id NUMBER(12),
metricType VARCHAR2(50),
year NUMBER(4),
value NUMBER
);
CREATE TYPE scival_tbl IS TABLE OF scival_row;
and then the pipelined function:
CREATE FUNCTION parseScival(
i_json CLOB,
i_year NUMBER
) RETURN scival_tbl PIPELINED DETERMINISTIC
IS
v_obj JSON_OBJECT_T := JSON_OBJECT_T.parse(i_json);
v_author JSON_OBJECT_T := v_obj.get_Object('author');
v_name VARCHAR2(100) := v_author.get_String('name');
v_id NUMBER(12) := v_author.get_Number('id');
v_metrics JSON_ARRAY_T := v_obj.get_Array('metrics');
v_metric JSON_OBJECT_T;
BEGIN
FOR i IN 0 .. v_metrics.Get_Size - 1 LOOP
v_metric := TREAT(v_metrics.get(i) AS JSON_OBJECT_T);
PIPE ROW(
scival_row(
v_name,
v_id,
v_metric.get_string('metricType'),
i_year,
v_metric.get_object('valueByYear').get_number(i_year)
)
);
END LOOP;
END;
/
Then you can use the query:
SELECT j.*
FROM table_name t
CROSS APPLY TABLE(parseScival(t.column_name, 2021)) j
Which outputs:
NAME
ID
METRICTYPE
YEAR
VALUE
Citizen, John
123456789
ScholarlyOutput
2021
1
db<>fiddle here

Related

How do I Update Oracle database by JSON data with 1 query?

I am trying to Update below sample json data into an Oracle version 19 table. (I want update 1000 rows from json with 1 query):
create table jt_test (
CUST_NUM int, SORT_ORDER int, CATEGORY varchar2(100)
);
[
{"CUST_NUM": 12345, "SORT_ORDER": 1, "CATEGORY": "ICE CREAM"}
{"CUST_NUM": 12345, "SORT_ORDER": 2, "CATEGORY": "ICE CREAM"}
{"CUST_NUM": 12345, "SORT_ORDER": 3, "CATEGORY": "ICE CREAM"}
]
I use this tutorial and this for insert rows from json and that work perfect. But for update rows I have no idea. How can I do?
Note: I use Oracle19C and connect and insert to db with cx_Oracle module python.
Code for Inserting by json to Oracle columns:
DECLARE
myJSON varchar2(1000) := '[
{"CUST_NUM": 12345, "SORT_ORDER": 1, "CATEGORY": "ICE CREAM"},
{"CUST_NUM": 12345, "SORT_ORDER": 2, "CATEGORY": "ICE CREAM"},
{"CUST_NUM": 12345, "SORT_ORDER": 3, "CATEGORY": "ICE CREAM"}
]';
BEGIN
insert into jt_test
select * from json_table ( myjson, '$[*]'
columns (
CUST_NUM, SORT_ORDER, CATEGORY
)
);
END;
In SQL Developer use below code :
MERGE INTO jt_test destttt using(
SELECT CUST_NUM,SORT_ORDER,CATEGORY FROM json_table (
'[
{"CUST_NUM": 12345, "SORT_ORDER": 1, "CATEGORY": "ICE CREAM"},
{"CUST_NUM": 12345, "SORT_ORDER": 2, "CATEGORY": "ICE CREAM"},
{"CUST_NUM": 12345, "SORT_ORDER": 3, "CATEGORY": "ICE CREAM"}
]'
,'$[*]'
COLUMNS
CUST_NUM int PATH '$.CUST_NUM ',
SORT_ORDER int PATH '$.SORT_ORDER ',
CATEGORY varchar2 PATH '$.CATEGORY ' ) ) srccccc
ON ( destttt.CUST_NUM= srccccc.CUST_NUM)
WHEN MATCHED THEN UPDATE SET destttt.CATEGORY=srccccc.CATEGORY
WHEN NOT MATCHED THEN INSERT ( CUST_NUM,SORT_ORDER,CATEGORY) VALUES (srccccc.CUST_NUM,srccccc.SORT_ORDER,srccccc.CATEGORY);
In python with cx_Oracle use below code :
long_json_string = '''[
{"CUST_NUM": 12345, "SORT_ORDER": 1, "CATEGORY": "ICE CREAM"},
{"CUST_NUM": 12345, "SORT_ORDER": 2, "CATEGORY": "ICE CREAM"},
{"CUST_NUM": 12345, "SORT_ORDER": 3, "CATEGORY": "ICE CREAM"}
]'''
sql = '''
DECLARE jsonvalue CLOB := :long_json_string ;
begin
MERGE INTO jt_test destttt using(
SELECT CUST_NUM,SORT_ORDER,CATEGORY FROM json_table (jsonvalue
,'$[*]'
COLUMNS
CUST_NUM int PATH '$.CUST_NUM',
SORT_ORDER int PATH '$.SORT_ORDER',
CATEGORY varchar2 PATH '$.CATEGORY' ) ) srccccc
ON ( destttt.CUST_NUM= srccccc.CUST_NUM)
WHEN MATCHED THEN UPDATE SET destttt.CATEGORY=srccccc.CATEGORY
WHEN NOT MATCHED THEN INSERT ( CUST_NUM,SORT_ORDER,CATEGORY) VALUES (srccccc.CUST_NUM,srccccc.SORT_ORDER,srccccc.CATEGORY);
'''
cursor.execute(sql, long_json_string=long_json_string)
Note1: Do not forget in end use commit.
Note 2: Make sure that the column you use as a comparison is not repeated in a json and causes deadlock.
Note 3: there is case sensitivity json keys, that is, CUST_NUM is different from cust_num and CUST_num and ...
Wrong : CUST_NUM int PATH '$.CUST_num' or CUST_NUM int PATH '$.cusr _num'
Ok: CUST_NUM int PATH '$.CUST_NUM'

Oracle JSON_QUERY with path as query column value

I try to get part of JSON column in each result row this select
SELECT TRIM(a.symbol),
TRIM(a.ex_name),
to_char(a.date_rw, 'dd-MON-yyyy'),
a.pwr,
a.last,
JSON_QUERY(b.mval, '$."-9"') as value
FROM adviser_log a
INNER JOIN profit_model_d b
ON a.date_rw = b.date_rw
WHERE a.date_rw = '08-OCT-2021'
select result:
VERY NAS 08-OCT-2021 -9 8.9443 {"sl":-3.6,"tp":5,"avg":1.368,"max":5,"min":-3.6,"count":1}
As a json path I put "-9" literal but I wanna put as path a.pwr is it possible
I tried put CONCAT('$.', a.pwr) without result
Is it any way to create dynamical json path into JSON_QUERy
I want to match part json which key compared with a.pwr to each row in select
Thx
You can use a function to dynamically get the JSON value:
WITH FUNCTION get_value(
value IN CLOB,
path IN VARCHAR2
) RETURN VARCHAR2
IS
BEGIN
RETURN JSON_OBJECT_T( value ).get_object( path ).to_string();
END;
SELECT TRIM(a.symbol) AS symbol,
TRIM(a.ex_name) AS ex_name,
to_char(a.date_rw, 'dd-MON-yyyy') AS date_rw,
a.pwr,
a.last,
get_value(b.mval, a.pwr) AS value
FROM adviser_log a
INNER JOIN profit_model_d b
ON a.date_rw = b.date_rw
WHERE a.date_rw = DATE '2021-10-08'
Which, for your sample data:
CREATE TABLE adviser_log (symbol, ex_name, date_rw, pwr, last) AS
SELECT 'VERY', 'NAS', DATE '2021-10-08', -9, 8.9443 FROM DUAL;
CREATE TABLE profit_model_d (date_rw DATE, mval CLOB CHECK (mval IS JSON));
INSERT INTO profit_model_d (
date_rw,
mval
) VALUES (
DATE '2021-10-08',
'{"-9":{"sl":-3.6,"tp":5,"avg":1.368,"max":5,"min":-3.6,"count":1}}'
);
Outputs:
SYMBOL
EX_NAME
DATE_RW
PWR
LAST
VALUE
VERY
NAS
08-OCT-2021
-9
8.9443
{"sl":-3.6,"tp":5,"avg":1.368,"max":5,"min":-3.6,"count":1}
db<>fiddle here

How to use Associative Arrays in PL/SQL in a WHERE Condition such that Key-Value pairs of Associative array are Column names in my table

I have a question regarding my problem in using of Associative arrays inside a sql query.
I use ORDS (Oracle Rest Data Services) as my RESTApi Server. I can define variables in the query string.
Imagine that I pass my variable like this in the query string:
https://MY-HOST-ADDRESS:PORT/rest/v1/list?MyVariable=Value
I defined my associative array as below:
DECLARE
TYPE xlatemap is TABLE OF VARCHAR2(64)
INDEX BY VARCHAR2(64);
col_xlate xlatemap;
BEGIN
col_xlate('address') := 'ci.CAMPUS';
col_xlate('hostname') := 'ci.VISIBLE_ID';
col_xlate('serial') := 'ci.SERIAL_NO';
What i would like to do is, getting user-friendly variable names (e.g. address, hostname and serial) in the query strings. Therefore I created this hashmap (associative_array).
My aim is to use this assoc array in my WHERE condition like below:
SELECT ci.CAMPIS, ci.VISIBLE_ID, ci.SERIAL_NO
FROM tableName as ci
WHERE col_xlate('hostname') like 'RT-101'
Do you know how can I use it inside
You cannot, an associative array is a PL/SQL data type and is not supported in SQL.
You can use collections and VARRAYs defined in the SQL scope in an SQL statement but not associative arrays as they can only be defined in a PL/SQL scope.
If you want the value from the associative array as the column name in the SQL then you will need to evaluate the value from the associative array in PL/SQL and use dynamic SQL to execute the generated query. (But you can't directly use the associative array in an SQL statement.)
DECLARE
TYPE xlatemap is TABLE OF VARCHAR2(64)
INDEX BY VARCHAR2(64);
col_xlate xlatemap;
p_campis TABLENAME.CAMPIS%TYPE;
p_visible_id TABLENAME.VISIBLE_ID%TYPE;
p_serial_no TABLENAME.SERIAL_NO%TYPE;
BEGIN
col_xlate('address') := 'ci.CAMPUS';
col_xlate('hostname') := 'ci.VISIBLE_ID';
col_xlate('serial') := 'ci.SERIAL_NO';
EXECUTE IMMEDIATE 'SELECT ci.CAMPIS, ci.VISIBLE_ID, ci.SERIAL_NO
FROM tableName ci
WHERE ' || col_xlate('hostname') || ' like ''RT-101'''
INTO p_campis, p_visible_id, p_serial_no;
-- this may need to be BULK COLLECT INTO if you are going to return multiple rows.
-- do something with the variables;
END;
/
If you want to do it in SQL then you'll need to use a collection (or VARRAY) defined in the SQL scope:
Oracle Setup:
CREATE TYPE key_value_pair AS OBJECT(
key VARCHAR2(64),
value VARCHAR2(64)
);
CREATE TYPE key_value_pair_table AS TABLE OF key_value_pair;
Test Data:
CREATE TABLE table_name ( CAMPIS, VISIBLE_ID, SERIAL_NO ) AS
SELECT 'A1', 'A2', 'A3' FROM DUAL UNION ALL
SELECT 'B1', 'B2', 'B3' FROM DUAL UNION ALL
SELECT 'C1', 'C2', 'C3' FROM DUAL
Query:
SELECT *
FROM table_name
WHERE CASE ( SELECT value
FROM TABLE(
key_value_pair_table(
key_value_pair( 'address', 'CAMPIS' ),
key_value_pair( 'hostname', 'VISIBLE_ID' ),
key_value_pair( 'serial', 'SERIAL_NO' )
)
)
WHERE key = 'hostname'
)
WHEN 'CAMPIS' THEN campis
WHEN 'VISIBLE_ID' THEN visible_id
WHEN 'SERIAL_NO' THEN serial_no
END
IN ( 'A2', 'B2' )
but it seems like you could just eliminate the array and just use a case statement:
SELECT *
FROM table_name
WHERE CASE 'hostname' -- your variable
WHEN 'address' THEN campis
WHEN 'hostname'THEN visible_id
WHEN 'serial' THEN serial_no
END
IN ( 'A2', 'B2' )
Output:
Both output:
CAMPIS | VISIBLE_ID | SERIAL_NO
:----- | :--------- | :--------
A1 | A2 | A3
B1 | B2 | B3
db<>fiddle here

ORA-40474: invalid UTF-8 byte sequence in JSON data

I am trying to parse JSON data to different columns in oracle. below is the sql I am running. I am not able to identify why I am getting json parse error. I have tried to replace non-ascii, non-printable characters but its still not working -
select * from (select '{
"data": [
{
"note": "Yeah, it still wonb\u00000019t let me. Not sure why.\r\n\r\nThanks man!\r\n\r",
"id": 0
}
]
}' json_data from dual)i ,json_table( i.json_data , '$.data[*]'
COLUMNS (
ID varchar2(4000) path '$.id',
note varchar2(4000) path '$.note'
) )

stored procedure for inserting data into multiple tables in database using servlet

How can I write a stored procedure for inserting multiple rows in an html table on a jsp page to an oracle database.
I have written a procedure in the following way, but it isn't working:
create or replace procedure insert_service_detail(
p_challandetailid in cm_challan_details.challandetailsid%type,
p_challanid in cm_challan_details.challanid%type,
p_serviceid in cm_challan_details.serviceid%type,
p_year in cm_challan_details.year%type,
p_month in cm_challan_details.month%TYPE,
p_fee in cm_challan_details.fee%type,
p_cess in cm_challan_details.cess%type,
p_penalty in cm_challan_details.penalty%type,
p_total in cm_challan_details.total%type) as
begin
insert into CM_CHALLAN_DETAILS(challanid, serviceid, year, month, fee, cess, penalty, total)
values(p_challandetailid, p_challanid, p_serviceid, p_year, p_month, p_fee, p_cess, p_penalty, p_total);
end insert_service_detail;
I found the answer for my question,
create or replace PROCEDURE INSERT_DETAILS
(
p_challanid in table1.CHALLANID%type,
p_ulbid in table1.ulbid%type,
p_challanno in table1.CHALLANNO%type,
p_challanDate in table1.CHALLANDATE%type,
p_createdby in table1.CREATEDBY%type,
p_createdon in table1.CREATEDON%type,
p_finyearid in table1.FINYEARID%type,
p_challanstatus in table1.CHALLANSTATUS%TYPE,
p_name in table2.APPLICANTNAME%type,
p_addr in table2.APPLICANTADDRESS%type,
p_email in table2.APPLICANTEMAIL%type,
p_mobile in table2.MOBILENO%type,
p_contact in table2.CONTACTNO%type,
p_challandetailid in table3.challandetailid%type,
p_details_challanid in table3.CHALLANID%type,
p_serviceid in table3.serviceid%type,
p_year in table3.year%type,
p_month in table3.month%TYPE,
p_fee in table3.fee%type,
p_cess in table3.cess%type,
p_penalty in table3.penalty%type,
p_total in table3.total%type
)
AS
BEGIN
insert into table1(challanid, ulbid, challanno, CHALLANDATE, createdby, createdon, finyearid, CHALLANSTATUS) VALUESVALUES( 2, 1002, 'chln02', p_challandate, 'asdasd', to_date('02/03/2015', 'dd/mm/yyyy'), 10002, 'Y');
insert into table2(challinid, APPLICANTNAME, APPLICANTADDRESS, APPLICANTEMAIL, MOBILENO, CONTACTNO) values( 2, 2, 'abcd', p_addr, p_email, p_mobile, p_contact);
insert into table3( challandetailid, challanid, serviceid, year, month, fee, cess, penalty, total) values( 2, 102, 2, p_year, p_month, 10.25, 0.25, 0.1, 10.61);
commit;
END INSERT_CHALLAN_DETAILS;
I got this values inserted into database and also inserted into 3 tables....
Number of arguments do not match in the INSERT query.. Please check.

Resources