Parsing JSON in Oracle with attribute longer than 4000 - json

I'm trying to parse JSON attribute with value longer than 4000 characters. This is my sample json:
[{
"id": "268edbb5d111",
"name": "Sample Product",
"status": "created",
"description": "this is sample product",
"productCharacteristic": [{
"name": "property_1",
"value": "abc",
"valueType": "String"
}, {
"name": "property_2",
"value": 123,
"valueType": "Number"
}, {
"name": "property_3",
"value": "2020-05-01T04:56:07.000+00:00",
"valueType": "Date"
}
]
}
]
and this is PL/SQL code:
FOR x IN (SELECT *
FROM json_table(l_response, '$[*]'
COLUMNS
id varchar2(255) PATH '$.id',
name varchar2(255) PATH '$.name',
description varchar2(255) PATH '$.description',
status varchar2(255) PATH '$.status',
productCharacteristic varchar2(4000) FORMAT JSON PATH '$.productCharacteristic'
)
) LOOP
-- do something
END LOOP;
The productCharacteristic attribute might be longer than 4000 but changing its datatype to VARCHAR2(32000) compiles the package with ORA-00910: specified length too long for its datatype and changing to CLOB returns ORA-40484: invalid data type for JSON_TABLE column. Is there maybe a way to pass the whole array of productCharacteristic as JSON and parse it inside of the loop?
Thanks.

Is there maybe a way to pass the whole array of productCharacteristic as JSON and parse it inside of the loop?
It might not be quite what you want, but you can get all the array elements flattened out with nested path:
FOR x IN (SELECT *
FROM json_table(l_response, '$[*]'
COLUMNS
id varchar2(255) PATH '$.id',
name varchar2(255) PATH '$.name',
description varchar2(255) PATH '$.description',
status varchar2(255) PATH '$.status',
nested path '$.productCharacteristic[*]'
COLUMNS (
product_name varchar2(255) PATH '$.name',
product_value varchar2(4000) PATH '$.value',
product_value_type varchar2(255) PATH '$.valueType'
)
)
) LOOP
-- do something
dbms_output.put_line(x.id || ' ' || x.product_name || ' ' || x.product_value);
END LOOP;
db<>fiddle

Related

Oracle Parse JSON Variable into table

I need to write a procedure which will accept a parameter of type CLOB, which will actually be a JSON string of text, parse that, and insert it into a table. The fields in the JSON are in the same order as the columns in the table.
The string would look like this:
{
"signal_id": "1",
"ts_id": "3",
"add_price": "0",
"qty": "1",
"stops": "0.00",
"yield": "0.00",
"close_date": "NULL",
"close_price": "0.00",
"ticker": "IBM",
"option_ticker": "NULL",
"signal_date": "2012-07-25",
"estimated_reporting_date": "NULL",
"signal_val": "1",
"comp_name": "INTERNATIONA",
"lt_price": "190.34",
"sell_target": "NULL",
"high_target": "NULL",
}
What is the best way to parse that, and insert into the table?
Use JSON_TABLE:
CREATE PROCEDURE insert_json (i_json IN CLOB)
IS
BEGIN
INSERT INTO your_table (
signal_id, ts_id, add_price, qty, stops, yield, close_date, close_price,
ticker, option_ticker, signal_date, estimated_reporting_date
/*...*/
)
SELECT *
FROM JSON_TABLE(
i_json,
'$'
COLUMNS(
signal_id NUMBER PATH '$.signal_id',
ts_id NUMBER PATH '$.ts_id',
add_price NUMBER PATH '$.add_price',
qty NUMBER PATH '$.qty',
stops NUMBER PATH '$.stops',
yield NUMBER PATH '$.yield',
close_date DATE PATH '$.close_date',
close_price NUMBER PATH '$.close_price',
ticker VARCHAR2(10) PATH '$.ticker',
option_ticker VARCHAR2(10) PATH '$.option_ticker',
signal_date DATE PATH '$.signal_date',
estimated_reporting_date DATE PATH '$.estimated_reporting_date'
-- ...
)
);
END insert_json;
/
db<>fiddle here

How do you run a query that returns a JSON array into a PL/SQL table?

I have the following code:
select tab.*
from (SELECT '{ "name":"John",
"age":30,
"cars": [
{"name":"Ford", "models":[ "Fiesta", "Focus", "Mustang"]},
{"name":"BMW", "models":[ "320", "X3", "X5"]},
{"name":"Fiat", "models":[ "500", "Panda"]}
]}' AS DATAx FROM dual),
JSON_TABLE(DATAx, '$'
columns ("A_json" varchar2(100) path '$.age'
,"B_check" varchar2(100) path '$.name'
,"C_check" varchar2(100) path '$.cars[*].name')) tab
which shows the following table when run:
A_json: 30
B_check: John
C_check: NULL
I would like to obtain all names from the cars array. How could I get the following output instead?:
A_json: 30
B_check: John
C_check: Ford
A_json: 30
B_check: John
C_check: BMW
A_json: 30
B_check: John
C_check: Fiat
If I try replacing * for indexes (1,2,3) it works for one row only, but I am not able to get them all.
Nested path needs to be defined to reach to 'cars--> name'. try the below given SQL -
select tab.*
from (SELECT '{ "name":"John",
"age":30,
"cars": [
{"name":"Ford", "models":[ "Fiesta", "Focus", "Mustang"]},
{"name":"BMW", "models":[ "320", "X3", "X5"]},
{"name":"Fiat", "models":[ "500", "Panda"]}
]}' AS DATAx FROM dual),
JSON_TABLE(DATAx, '$'
columns ("A_json" varchar2(100) path '$.age'
,"B_check" varchar2(100) path '$.name'
,"C_check" varchar2(100) path '$.cars.name'
, nested path '$.cars[*]' columns(
name number path '$.name'))
) tab

SQL Server For JSON Path dynamic column name

We are exploring the JSON feature in SQL Sever and for one of the scenarios we want to come up with a SQL which can return a JSON like below
[
{
"field": {
"uuid": "uuid-field-1"
},
"value": {
"uuid": "uuid-value" //value is an object
}
},
{
"field": {
"uuid": "uuid-field-2"
},
"value": "1". //value is simple integer
}
... more rows
]
The value field can be a simple integer/string or a nested object.
We are able to come up with a table which looks like below:
field.uuid | value.uuid | value|
------------|---------- | -----|
uuid-field-1| value-uuid | null |
uuid-field-2| null | 1 |
... more rows
But as soon as we apply for json path, it fails saying
Property 'value' cannot be generated in JSON output due to a conflict with another column name or alias. Use different names and aliases for each column in SELECT list.
Is it possible to do it somehow generate this? The value will either be in the value.uuid or value not both?
Note: We are open to possibility of if we can convert each row to individual JSON and add all of them in an array.
select
json_query((select v.[field.uuid] as 'uuid' for json path, without_array_wrapper)) as 'field',
value as 'value',
json_query((select v.[value.uuid] as 'uuid' where v.[value.uuid] is not null for json path, without_array_wrapper)) as 'value'
from
(
values
('uuid-field-1', 'value-uuid1', null),
('uuid-field-2', null, 2),
('uuid-field-3', 'value-uuid3', null),
('uuid-field-4', null, 4)
) as v([field.uuid], [value.uuid], value)
for json auto;--, without_array_wrapper;
The reason for this error is that (as is mentioned in the documentation) ... FOR JSON PATH clause uses the column alias or column name to determine the key name in the JSON output. If an alias contains dots, the PATH option creates nested objects. In your case value.uuid and value both generate a key with name value.
I can suggest an approach (probably not the best one), which uses JSON_MODIFY() to generate the expected JSON from an empty JSON array:
Table:
CREATE TABLE Data (
[field.uuid] varchar(100),
[value.uuid] varchar(100),
[value] int
)
INSERT INTO Data
([field.uuid], [value.uuid], [value])
VALUES
('uuid-field-1', 'value-uuid', NULL),
('uuid-field-2', NULL, 1),
('uuid-field-3', NULL, 3),
('uuid-field-4', NULL, 4)
Statement:
DECLARE #json nvarchar(max) = N'[]'
SELECT #json = JSON_MODIFY(
#json,
'append $',
JSON_QUERY(
CASE
WHEN [value.uuid] IS NOT NULL THEN (SELECT d.[field.uuid], [value.uuid] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
WHEN [value] IS NOT NULL THEN (SELECT d.[field.uuid], [value] FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
END
)
)
FROM Data d
SELECT #json
Result:
[
{
"field":{
"uuid":"uuid-field-1"
},
"value":{
"uuid":"value-uuid"
}
},
{
"field":{
"uuid":"uuid-field-2"
},
"value":1
},
{
"field":{
"uuid":"uuid-field-3"
},
"value":3
},
{
"field":{
"uuid":"uuid-field-4"
},
"value":4
}
]

How to get array index from Oracle JSON column?

Having a JSON like this (I know that JSON doesn't support comments. Used in this case to illustrate the idea):
{
"people": [
{ --// <-- index 0
"id": 100,
"name": "John Doe"
},
{ --// <-- index 1
"id": 101,
"name": "Jane Roe"
}
]
}
We can select values from specific elements in the array doing something like this:
SELECT name
FROM JSON_TABLE(
'{
"people": [
{
"id": 100,
"name": "John Doe"
},
{
"id": 101,
"name": "Jane Roe"
},
]
}', '$.people[*]'
COLUMNS(
ID NUMBER PATH '$.id',
NAME VARCHAR2 PATH '$.name'
)
) info
WHERE info.id = 101
Result:
NAME
--------
Jane Roe
Is there a way to get the element index in the array? Something like:
SELECT array_index --// <-- how get the array index of the element found?
FROM JSON_TABLE(
--// ...
) info
WHERE info.id = 101
Result:
ARRAY_INDEX
-----------
1
Is possible to do something like this using JSON support in Oracle 12c?
COLUMNS(
idx FOR ORDINALITY,
ID NUMBER PATH '$.id',
NAME VARCHAR2 PATH '$.name'
)
should work for you
As ArtBajji wrote, the rows itself dont have a index. Therefor you need to create a "fake id" that is relatively fixed.
This can be achieved by giving your data a order(order by id) and a index (rownum). Then select from this "modified" table
SELECT name, indx FROM (
SELECT ind.*, rownum "INDX"
FROM JSON_TABLE(
'{
"people": [
{
"id": 100,
"name": "John Doe"
},
{
"id": 101,
"name": "Jane Roe"
},
]
}', '$.people[*]'
COLUMNS(
ID NUMBER PATH '$.id',
NAME VARCHAR2 PATH '$.name'
)
) ind order by id) tab
WHERE tab.id = 101
Yields 1 for id 100 and 2 for id 101.
Be aware that the index of the elements 100 and 101 grow by one if you insert a item with a id < 100.

JSON Oracle SQL parsing / unnest embedded JSON data in escaped form

Here is my JSON stored in a CLOB column:
select upJSON from myLocations;
{"values":[
{"nameValuePairs":{"upJSON":"{\"mResults\":[0.0,0.0],\"mProvider\":\"fused\",\"mDistance\":0.0,\"mAltitude\":0.0}","id":"1","updated":"2015-03-30 20:28:51"}},
{"nameValuePairs":{"upJSON":"{\"mResults\":[0.0,0.0],\"mProvider\":\"FINDME\",\"mDistance\":0.0,\"mAltitude\":22.2}","id":"2","updated":"2015-03-30 20:28:53"}},
{"nameValuePairs":{"upJSON":"{\"mResults\":[0.0,0.0],\"mProvider\":\"fused\",\"mDistance\":0.0,\"mAltitude\":0.0}","id":"3","updated":"2015-03-30 20:28:55"}},
{"nameValuePairs":{"upJSON":"{\"mResults\":[0.0,0.0],\"mProvider\":\"fused\",\"mDistance\":0.0,\"mAltitude\":0.0}","id":"4","updated":"2015-03-30 20:28:57"}}
]}
(I have inserted newlines for clarity)
Please: What is the SQL (or PL/SQL) needed to select just the value of mProvider, mAltitude, and the id from the 2nd "nameValuePairs"
(= "FINDME" and 22.2 and "2") in the example above)
??
Since you're using 12c you have access to the native JSON parsing (as long as your CLOB column has an is json check constraint).
Some good background is available at:
https://docs.oracle.com/database/121/ADXDB/json.htm#ADXDB6371
If you're JSON looks something like this:
{
"values": [
{
"nameValuePairs": {
"upJSON": {
"mResults": [
"0.0",
"0.0"
],
"mProvider": "fused",
"mDistance": "0.0",
"mAltitude": "22.2"
},
"id": "1",
"updated": "2015-03-30 20:28:51"
}
},
...
...
Although, when I put your snippet from the question into JSONLint it returns:
{
"values": [
{
"nameValuePairs": {
"upJSON": "{\"mResults\":[0.0,0.0],\"mProvider\":\"fused\",\"mDistance\":0.0,\"mAltitude\":0.0}",
"id": "1",
"updated": "2015-03-30 20:28:51"
}
},
{
"nameValuePairs": {
"upJSON": "{\"mResults\":[0.0,0.0],\"mProvider\":\"FINDME\",\"mDistance\":0.0,\"mAltitude\":22.2}",
"id": "2",
"updated": "2015-03-30 20:28:53"
}
},
Something like the following might get you started:
select
upJSON.values
from
myLocations
where
json_value(upJSON, '$nameValuePairs.id' returning varchar2 error on error) = '2';
If you want to limit the query to a single ID, you'll need to add a full-text or function-based index to the JSON column.
https://odieweblog.wordpress.com/2015/04/12/json_table-chaining/#comment-1025
with tmp as (
SELECT /*+ no_merge */ d.*
FROM ulocations ul,
json_table(ul.upjson, '$'
columns(
NESTED PATH '$.values[*].nameValuePairs'
COLUMNS (
updated VARCHAR2(19 CHAR) PATH '$.updated'
, id varchar2(9 char) path '$._id'
, upJSON VARCHAR2(2000 CHAR) PATH '$.upJSON'
)) ) d
--where d.id = '0'
)
select t.updated
, t.id
, jt2.*
from tmp t
, json_table(t.upJSON, '$'
columns mProvider varchar2(5) path '$.mProvider'
, mLongitude number path '$.mLongitude'
) jt2
;