Creating json from Oracle database table - sql

Hi I have to create json file from Oracle table. I have data in the below form.
I want data in this format.
{
"add" :
[
{
"canonicalName" : "Apple Computers",
"synonyms" :
[
"Apple",
"Apple Inc"
]
},
{
"canonicalName" : "Google India",
"synonyms" :
[
"Google"
]
},
{
"canonicalName" : "IBM",
"synonyms" :
[
"IBM Corporation"
]
}
],
"delete" :
[
{
"canonicalName" : "IBM",
"synonyms" :
[
"IBM Corporation"
]
},
{
"canonicalName" : "TCS"
}
],
"update" :
[
{
"canonicalName" : "Infosys",
"synonyms" :
[
"Infosys Tech"
]
},
{
"canonicalName" : "Wipro Tech",
"synonyms" :
[
"Wipro Technology"
]
}
]
}
the below code is working properly.
with
prep (operation, orgname, fragment) as (
select operation, orgname,
json_object( key 'canonicalName' value orgname,
key 'synonyms'
value nullif(json_arrayagg(synonyms order by synonyms), '[]')
FORMAT JSON ABSENT ON NULL
)
from t
group by orgname, operation
)
select json_objectagg( key operation
value json_arrayagg(fragment order by orgname)
) as json_str
from prep
group by operation;
Now I have to add one extra column in this table.
so column tablename contains "ORG" and "ITEM" values. so I have to create 2 files one would be item.json and another one would be ORG.json and so on.
I need to put data which has ITEM in item.json and which has ORG in ORG.json.
what changes i need to do in above query.
Even PL/SQL would be OK. Can you suggest changed on above query?
It would be also fine if we can store the result into some array and return to calling environment

Here is one approach. You don't need to know the values in the TABLENAME column in advance. Rather, the query output will have one row per unique value in TABLENAME, presented in two columns: the TABLENAME and the corresponding JSON string for that TABLENAME.
with
prep1 (tablename, operation, orgname, fragment) as (
select tablename, operation, orgname,
json_object( key 'canonicalName' value orgname,
key 'synonyms'
value nullif(json_arrayagg(synonyms order by synonyms), '[]')
FORMAT JSON ABSENT ON NULL
)
from t
group by tablename, orgname, operation
)
, prep2 (tablename, operation, org_str) as (
select tablename, operation, json_arrayagg(fragment order by orgname)
from prep1
group by tablename, operation
)
select tablename, json_objectagg(key operation value org_str) as json_str
from prep2
group by tablename
;
TABLENAME JSON_STR
--------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ITEM {"add":[{"canonicalName":"Apple Computers","synonyms":["Apple","Apple Inc"]},{"canonicalName":"Google India","synonyms":["Google"]},{"canonicalName":"IBM","synonyms":["IBM Corporation"]}],"update":[{"canonicalName":"Infosys","synonyms":["Infosys Tech"]},{"canonicalName":"Wipro Tech","synonyms":["Wipro Technology"]}],"delete":[{"canonicalName":"IBM","synonyms":["IBM Corporation"]},{"canonicalName":"TCS"}]}
ORG {"add":[{"canonicalName":"Apple Computers","synonyms":["Apple","Apple Inc"]},{"canonicalName":"Google India","synonyms":["Google"]},{"canonicalName":"IBM","synonyms":["IBM Corporation"]}],"update":[{"canonicalName":"Infosys","synonyms":["Infosys Tech"]},{"canonicalName":"Wipro Tech","synonyms":["Wipro Technology"]}],"delete":[{"canonicalName":"IBM","synonyms":["IBM Corporation"]},{"canonicalName":"TCS"}]}

Related

Parsing nested JSON fields in Snowflake

I am pretty new to Snowflake and I am now trying to parse a JSON field and pull its attributes to return in the response.
I tried a few variations but every time, the attribute is populating as null.
attributes column in my table has this JSON:
{
"Status": [
"ACTIVE"
],
"Coverence": [
{
"Sub": [
{
"EndDate": [
"2020-06-22"
],
"Source": [
"Test"
],
"Id": [
"CovId1"
],
"Type": [
"CovType1"
],
"StartDate": [
"2019-06-22"
],
"Status": [
"ACTIVE"
]
}
]
}
]
}
What I tried:
SELECT DISTINCT *
from
(
TRIM(mt."attributes":Status, '[""]')::string as STATUS,
TRIM(r.value:"Sub"."Id", '[""]')::string as ID,
TRIM(r.value:"Sub"."Source", '[""]')::string as SOURCE
from "myTable" mt,
lateral flatten ( input => mt."attributes":"Coverence", outer => true) r
)
GROUP BY
STATUS,
ID,
SOURCE;
Later I tried:
SELECT DISTINCT *
from
(
TRIM(mt."attributes":Status, '[""]')::string as STATUS,
TRIM(r.value:"Id", '[""]')::string as ID,
TRIM(r.value:"Source", '[""]')::string as SOURCE
from "myTable" mt,
lateral flatten ( input => mt."attributes":"Coverence":"Sub", outer => true) r
)
GROUP BY
STATUS,
ID,
SOURCE;
But nothing worked. The STATUS is populating as expected. But ID and SOURCE are populating null.
Am I missing something or have I done something dumb? Please shed some light.
Assuming that Coverence could contain multiple Sub, therefore FLATTEN twice. At lowest level only first element is chosen (EndDate[0], Source[0] etc):
SELECT
mt."attributes":Status[0]::TEXT AS Status
,r2.value:EndDate[0]::TEXT AS EndDate
,r2.value:Source[0]::TEXT AS Source
,r2.value:Id[0]::TEXT AS Id
FROM myTable AS mt,
LATERAL FLATTEN(input => mt."attributes",
path => 'Coverence',
outer => true) r1,
LATERAL FLATTEN(input => r1.value,
path => 'Sub',
outer => true) r2;
Output:
All your elements are array type and the overall JSON is not making much sense... so to access all your individual elements, you have to use [] notation and then you can access the element values. You don't need to use flatten also, if you just have to access individual elements via index.

How do I use BigQuery DML to transform some fields of a struct nested within an array, within a struct, within an array?

I think this is a more complex version of the question in Update values in struct arrays in BigQuery.
I'm trying to update some of the fields in a struct, where the struct is heavily nested. I'm having trouble creating the SQL to do it. Here's my table schema:
CREATE TABLE `my_dataset.test_data_for_so`
(
date DATE,
hits ARRAY<STRUCT<search STRUCT<query STRING, other_column STRING>, metadata ARRAY<STRUCT<key STRING, value STRING>>>>
);
This is what the schema looks like in the BigQuery GUI after I create the table:
Here's the data I've inserted:
INSERT INTO `my_dataset.test_data_for_so` (date, hits)
VALUES (
CAST('2021-01-01' AS date),
[
STRUCT(
STRUCT<query STRING, other_column STRING>('foo bar', 'foo bar'),
[
STRUCT<key STRING, value STRING>('foo bar', 'foo bar')
]
)
]
)
My goal is to transform the "search.query" and "metadata.value" fields. For example, uppercasing them, leaving every other column (and every other struct field) in the row unchanged.
I'm looking for a solution involving either manually specifying each column in the SQL, or preferably, one where I can only mention the columns/fields I want to transform in the SQL, omitting all other columns/fields. This is a minimal example. The table I'm working on in production has hundreds of columns and fields.
For example, that row, when transformed this way, would change from:
[
{
"date": "2021-01-01",
"hits": [
{
"search": {
"query": "foo bar",
"other_column": "foo bar"
},
"metadata": [
{
"key": "foo bar",
"value": "foo bar"
}
]
}
]
}
]
to:
[
{
"date": "2021-01-01",
"hits": [
{
"search": {
"query": "FOO BAR",
"other_column": "foo bar"
},
"metadata": [
{
"key": "foo bar",
"value": "FOO BAR"
}
]
}
]
}
]
preferably, one where I can only mention the columns/fields I want to transform in the SQL ...
Use below approach - it does exactly what you wish - ONLY those fields that are to be updated are in use, all other (tens or hundreds ...) are preserved as is
update your_table
set hits = array(
select as struct *
replace(
(select as struct * replace (upper(query) as query) from unnest([search])) as search,
array(select as struct * replace(upper(value) as value) from unnest(metadata)) as metadata
)
from unnest(hits)
)
where true;
if applied to sample data in your question - result is

Joining tables with filter condition on multiple columns in Oracle DBMS

{
"description": "test",
"id": "1",
"name": "test",
"prod": [
{
"id": "1",
"name": "name",
"re": [
{
"name": "name1",
"value": "1"
},
{
"name": "name2",
"value": "1"
},
{
"name": "name3",
"value": "0"
},
{
"name": "name4",
"value": "0"
}
]
}
]
}
Here is the best I can do with your JSON input and your sample output.
Note that your document has a unique "id" and "name" ("1" and "test" in your example). Then it has an array named "productSpecificationRelationship". Each element of this array is an object with its own "id" - in the query, I show this id with the column name PSR_ID (PSR for Product Specification Relationship). Also, each object in this first-level array contains a sub-array (second level), with objects with "name" ("name" again!) and "value" keys. (This looks very much like an entity-attribute-value model - very poor practice.) In the intermediate step in my query (before pivoting), I call these RC_NAME and RC_VALUE (RC for Relationship Characteristic).
In your sample output you have more than one value in the ID and NAME columns. I don't see how that is possible; perhaps from unpacking more than one document? The JSON document you shared with us has "id" and "name" as top-level attributes.
In the output, I understand (or rather, assume, since I didn't understand too much from your question) that you should also include the PSR_ID - there is only one in your document, with value "10499", but in principle there may be more than one, and the output will have one row per such id.
Also, I assume the "name" values are limited to the four you mentioned (or, if there can be more, you are only interested in those four in the output).
With all that said, here is the query. Note that I called the table ES for simplicity. Also, you will see that I had to go to nested path twice (since your document includes an array of arrays, and I wanted to pick up the PSR_ID from the outer array and the tokens from the nested arrays).
TABLE SETUP
create table es (payloadentityspecification clob
check (payloadentityspecification is json) );
insert into es (payloadentityspecification) values (
'{
"description": "test",
"id": "1",
"name": "test",
"productSpecificationRelationship": [
{
"id": "10499",
"relationshipType": "channelRelation",
"relationshipCharacteristic": [
{
"name": "out_of_home",
"value": "1"
},
{
"name": "out_of_home_ios",
"value": "1"
},
{
"name": "out_of_home_android",
"value": "0"
},
{
"name": "out_of_home_web",
"value": "0"
}
]
}
]
}');
commit;
QUERY
with
prep (id, name, psr_id, rc_name, rc_value) as (
select id, name, psr_id, rc_name, rc_value
from es,
json_table(payloadentityspecification, '$'
columns (
id varchar2(10) path '$.id',
name varchar2(40) path '$.name',
nested path '$.productSpecificationRelationship[*]'
columns (
psr_id varchar2(10) path '$.id',
nested path '$.relationshipCharacteristic[*]'
columns (
rc_name varchar2(50) path '$.name',
rc_value varchar2(50) path '$.value'
)
)
)
)
)
select id, name, psr_id, ooh, ooh_android, ooh_ios, ooh_web
from prep
pivot ( min(case rc_value when '1' then 'TRUE'
when '0' then 'FALSE' else 'UNDEFINED' end)
for rc_name in ( 'out_of_home' as ooh,
'out_of_home_android' as ooh_android,
'out_of_home_ios' as ooh_ios,
'out_of_home_web' as ooh_web
)
)
;
OUTPUT
ID NAME PSR_ID OOH OOH_ANDROID OOH_IOS OOH_WEB
-- ---- ------ ----------- ----------- ----------- -----------
1 test 10499 TRUE FALSE TRUE FALSE
Conditional aggregation might be used in order to pivot the result set after extracting the values by using JSON_TABLE() and JSON_VALUE() functions such as
SELECT JSON_VALUE(payloadentityspecification, '$.name') AS channel_map_name,
MAX(CASE WHEN name = 'out_of_home' THEN
DECODE(value,1,'TRUE',0,'FALSE','UNDEFINED')
END) AS ooh,
MAX(CASE WHEN name = 'out_of_home_android' THEN
DECODE(value,1,'TRUE',0,'FALSE','UNDEFINED')
END) AS ooh_android,
MAX(CASE WHEN name = 'out_of_home_ios' THEN
DECODE(value,1,'TRUE',0,'FALSE','UNDEFINED')
END) AS ooh_ios,
MAX(CASE WHEN name = 'out_of_home_web' THEN
DECODE(value,1,'TRUE',0,'FALSE','UNDEFINED')
END) AS ooh_web
FROM EntitySpecification ES,
JSON_TABLE (payloadentityspecification, '$.productSpecificationRelationship[*]'
COLUMNS ( NESTED PATH '$.relationshipCharacteristic[*]'
COLUMNS (
description VARCHAR2(250) PATH '$.description',
name VARCHAR2(250) PATH '$.name',
value VARCHAR2(250) PATH '$.value'
)
)) jt
WHERE payloadentityspecification IS JSON
GROUP BY JSON_VALUE(payloadentityspecification, '$.name')
Demo

Transform JSON map to an array in Postgres via update statement

I have a postgresql table called datasource with jsonb column called data. It has the following structure:
{
"key1":{"param1" : "value1", "param2" : "value2"},
"key2":{"param2_1" : "value2_1", "param2_2" : "value2_2"},
"key3":{"param3_1" : "value3_1", "param3_2" : "value3_2"}
}
Is there any way to write some UPDATE script to transform given JSON to the following:
[
{"key": "key1", "param1" : "value1", "param2" : "value2"},
{"key": "key2", "param2_1" : "value2_1", "param2_2" : "value2_2"},
{"key": "key3", "param3_1" : "value3_1", "param3_2" : "value3_2"}
]
You can unnest the object to rows in a lateral join, then aggregate back into an array:
select d.*, x.*
from datasource d
cross join lateral (
select jsonb_agg(jsonb_build_object('key', j.k) || j.v) new_data
from jsonb_each(d.data) as j(k, v)
) x
Demo on DB Fiddle - with jsonb_pretty() enabled:
If you wanted an update statement:
update datasource d
set data = (
select jsonb_agg(jsonb_build_object('key', j.k) || j.v)
from jsonb_each(d.data) as j(k, v)
)

Postgres: How to alter jsonb value type for each element in an array?

I have a jsonb column named items in Postgres 10.12 like this:
{
"items": [
{
"itemQty": 2,
"itemName": "snake"
},
{
"itemQty": 1,
"itemName": "x kodiyum"
}
]
}
Now I want to convert itemQty type to string for every array element so that the new values are like this:
{
"items": [
{
"itemQty": "2",
"itemName": "snake"
},
{
"itemQty": "1",
"itemName": "x kodiyum"
}
]
}
How do I do this? I have gone through the documentation for Postgres jsonb and couldn't figure out.
On the server-side, I am using Spring boot and Hibernate with com.vladmihalcea.hibernate.type.json (Hibernate Types 52) if it helps.
Thanks
You could unnest the array, modify the elements, and then rebuild it. Assuming that the primary key of your table is id, that would be:
select jsonb_build_object(
'items', jsonb_agg(
jsonb_build_object(
'itemQty', (x.obj ->> 'itemQty')::text,
'itemName', x.obj ->> 'Name'
)
)
)new_items
from mytable t
cross join lateral jsonb_array_elements(t.items -> 'items') as x(obj)
group by id
Note that the explicit cast to ::text is not really needed here, as ->> extract text values anyway: I kept it because it makes the intent clearer.
If you want an update statement:
update mytable t
set items = (
select jsonb_build_object(
'items', jsonb_agg(
jsonb_build_object(
'itemQty', (x.obj ->> 'itemQty')::text,
'itemName', x.obj ->> 'Name'
)
)
)
from jsonb_array_elements(t.items -> 'items') as x(obj)
)
Demo on DB Fiddle