select key-value pairs from json as rows in Snowflake - json

Assume I have the following JSON:
{
"CURRENCY_CODE": "INR",
"CURRENCY_NAME": "Indian Rupee",
"ID_CURRENCY": 8,
"ISO_CODE": 4217
}
I want to query it in Snowflake so I get the following output:
Key
Value
CURRENCY_CODE
INR
CURRENCY_NAME
Indian Rupee
ID_CURRENCY
8
ISO_CODE
4217
I expect something like:
select d.key, d.value
from table('Here goes json') d
Does Snowflake have any function to achieve this?

It is possible to pass the argument directly into FLATTEN:
SELECT f.*
FROM TABLE(FLATTEN(INPUT => PARSE_JSON('<here goes json>'))) f;

You can use LATERAL FLATTEN() in your FROM clause to achieve this.
As an example:
SELECT fdt.KEY, fdt.VALUE
FROM VALUES('{
"CURRENCY_CODE": "INR",
"CURRENCY_NAME": "Indian Rupee",
"ID_CURRENCY": 8,
"ISO_CODE": 4217
}') dt
,lateral flatten( input => parse_json(column1) ) fdt;
KEY
VALUE
CURRENCY_CODE
INR
CURRENCY_NAME
Indian Rupee
ID_CURRENCY
8
ISO_CODE
4217

Related

How to Convert Oracle to Postgresql [duplicate]

I'm trying to migrate Oracle 12c queries to Postgres11.5.
Here is the json:
{
"cost": [{
"spent": [{
"ID": "HR",
"spentamount": {
"amount": 2000.0,
"country": "US"
}
}]
}],
"time": [{
"spent": [{
"ID": "HR",
"spentamount": {
"amount": 308.91,
"country": "US"
}
}]
}]
}
Here is the query that has to be migrated to Postgres 11.5:
select js.*
from P_P_J r,
json_table(r.P_D_J, '$.*[*]'
COLUMNS(NESTED PATH '$.spent[*]'
COLUMNS(
ID VARCHAR2(100 CHAR) PATH '$.ID',
amount NUMBER(10,4) PATH '$.spentamount.amount',
country VARCHAR2(100 CHAR) PATH '$.spentamount.country'))
) js
The result:
ID, amount, country
HR, 2000.0,US
HR,308.91,US
I have two questions here:
What does $.*[*] mean?
How can we migrate this query in Postgres so that it directly looks at 'spent' instead of navigating 'cost'->'spent' or 'time'->'spent'
There is no direct replacement for json_table in Postgres. You will have to combine several calls to explode the JSON structure.
You didn't show us your expected output, but as far as I can tell, the following should do the same:
select e.item ->> 'ID' as id,
(e.item #>> '{spentamount, amount}')::numeric as amount,
e.item #>> '{spentamount, country}' as country
from p_p_j r
cross join jsonb_each(r.p_d_j) as a(key, val)
cross join lateral (
select *
from jsonb_array_elements(a.val)
where jsonb_typeof(a.val) = 'array'
) as s(element)
cross join jsonb_array_elements(s.element -> 'spent') as e(item)
;
The JSON path expression '$.*[*] means: iterate over all top-level keys, then iterate over all array elements found in there and the nested path '$.spent[*]' then again iterates over all array elements in there. These steps are reflected in the three JSON function calls that are needed to get there.
With Postgres 12, this would be a bit easier as this can be done with a single call to jsonb_path_query() which also use a JSON Path to access the elements using a very similar JSON path expression:
select e.item ->> 'ID' as id,
(e.item #>> '{spentamount, amount}')::numeric as amount,
e.item #>> '{spentamount, country}' as country
from p_p_j r
cross join jsonb_path_query(r.p_d_j, '$.*[*].spent[*]') as e(item)
;
Online example

Select - Oracle JSON Object - Join

I have a requirement to select column values in Oracle in a JSON structure. Let me explain the requirement in detail
We have a table called "dept" that has the following rows
There is another table called "emp" that has the following rows
The output we need is as follows
{"Data": [{
"dept": "Sports",
"City": "LA",
"employees": {
"profile":[
{"name": "Ben John", "salary": "15000"},
{"name": "Carlos Moya", "salary": "19000"}]
}},
{"dept": "Sales",
"City": "Miami",
"employees": {
"profile":[
{"name": "George Taylor", "salary": "9000"},
{"name": "Emma Thompson", "salary": "8500"}]
}}
]
}
The SQL that I issued is as follows
select json_object('dept' value b.deptname,
'city' value b.deptcity,
'employees' value json_object('employee name' value a.empname,
'employee salary' value a.salary)
format json) as JSONRETURN
from emp a, dept b where
a.deptno=b.deptno
However, the result looks like the following and not what we expected.
Please note the parent data is repeated. What is the mistake I am making?
Thanks for the help
Bala
You can do something like this. Note the multiple (nested) calls to json_object and json_arrayagg. Tested in Oracle 12.2; other versions may have other tools that can make the job easier.
select json_object(
'Data' value
json_arrayagg(
json_object (
'dept' value deptname,
'City' value deptcity,
'employees' value
json_object(
'profile' value
json_arrayagg(
json_object(
'name' value empname,
'salary' value salary
) order by empid -- or as needed
)
)
) order by deptno -- or as needed
)
) as jsonreturn
from dept join emp using (deptno)
group by deptno, deptname, deptcity
;

how to maintain order of elements using snowflake object_construct() function instead of sorting by the keys?

Following snowflake query returns the JSON structure but output is sorted by the keys. How not to sort by the keys but retains the order? Is there any parameter setting that needs to be set?
select
object_construct
(
'entity', 'XYZ',
'allowed', 'Yes',
'currency', 'USD',
'statement_month','July, 2020'
)
Output: --it sorts by the keys
{
"allowed": "Yes",
"currency": "USD",
"entity": "XYZ",
"statement_month": "July, 2020"
}
Expected Output: --same order as specified
{
"entity": "XYZ",
"allowed": "Yes",
"currency": "USD",
"statement_month": "July, 2020"
}
JSON is an unordered collection of name and values. Order cannot be guaranteed in JSON.
The constructed object does not necessarily preserve the original order of the key-value pairs.
You can do it like as below
SELECT mytable:entity::string as entity,
mytable:allowed::string as allowed,
mytable:currency::string as currency,
mytable:statement_month::string as statement_month
from
(select
object_construct
(
'entity', 'XYZ',
'allowed', 'Yes',
'currency', 'USD',
'statement_month','July, 2020'
) mytable);
Unfortunately, no
Usage notes:
https://docs.snowflake.com/en/sql-reference/functions/object_construct.html#usage-notes
The constructed object does not necessarily preserve the original order of the key-value pairs.
same for PARSE_JSON Usage notes:
https://docs.snowflake.com/en/sql-reference/functions/parse_json.html#usage-notes
The order of the key-value pairs in the string produced by TO_JSON is not predictable.
The order was found to be maintained when using object_construct(*):
WITH base AS (
SELECT 'XYZ' "entity", 'Yes' "allowed", 'USD' "currency", 'July, 2020' "statement_month")
SELECT object_construct(*) FROM base;

Oracle JSON_TABLE to PostgreSQL - how to search from the second hierarchical key in a JSON column

I'm trying to migrate Oracle 12c queries to Postgres11.5.
Here is the json:
{
"cost": [{
"spent": [{
"ID": "HR",
"spentamount": {
"amount": 2000.0,
"country": "US"
}
}]
}],
"time": [{
"spent": [{
"ID": "HR",
"spentamount": {
"amount": 308.91,
"country": "US"
}
}]
}]
}
Here is the query that has to be migrated to Postgres 11.5:
select js.*
from P_P_J r,
json_table(r.P_D_J, '$.*[*]'
COLUMNS(NESTED PATH '$.spent[*]'
COLUMNS(
ID VARCHAR2(100 CHAR) PATH '$.ID',
amount NUMBER(10,4) PATH '$.spentamount.amount',
country VARCHAR2(100 CHAR) PATH '$.spentamount.country'))
) js
The result:
ID, amount, country
HR, 2000.0,US
HR,308.91,US
I have two questions here:
What does $.*[*] mean?
How can we migrate this query in Postgres so that it directly looks at 'spent' instead of navigating 'cost'->'spent' or 'time'->'spent'
There is no direct replacement for json_table in Postgres. You will have to combine several calls to explode the JSON structure.
You didn't show us your expected output, but as far as I can tell, the following should do the same:
select e.item ->> 'ID' as id,
(e.item #>> '{spentamount, amount}')::numeric as amount,
e.item #>> '{spentamount, country}' as country
from p_p_j r
cross join jsonb_each(r.p_d_j) as a(key, val)
cross join lateral (
select *
from jsonb_array_elements(a.val)
where jsonb_typeof(a.val) = 'array'
) as s(element)
cross join jsonb_array_elements(s.element -> 'spent') as e(item)
;
The JSON path expression '$.*[*] means: iterate over all top-level keys, then iterate over all array elements found in there and the nested path '$.spent[*]' then again iterates over all array elements in there. These steps are reflected in the three JSON function calls that are needed to get there.
With Postgres 12, this would be a bit easier as this can be done with a single call to jsonb_path_query() which also use a JSON Path to access the elements using a very similar JSON path expression:
select e.item ->> 'ID' as id,
(e.item #>> '{spentamount, amount}')::numeric as amount,
e.item #>> '{spentamount, country}' as country
from p_p_j r
cross join jsonb_path_query(r.p_d_j, '$.*[*].spent[*]') as e(item)
;
Online example

List arbitrary number of elements from a json field (postgres)

I have a json field in postgres db that contains data like
[{
"value": "+1 968 730 5680",
"label": "mobile",
"primary": true
},{
"value": "+1 909 169 4444",
"label": "mobile",
"primary": false
}]
I want to query this field to produce
+1 968 730 5680; +1 909 169 4444
Given that the number of elemets can vary.
I had enough brains to get the first element with
json_field -> 0 ->> 'value'
but am totally stuck at producing a string with all elements.
Please help.
UPDATE
Following advice below, I get an error with statement
select d ->> 'value' as val
from analyst.person
cross join jsonb_array_elements(phone) as x(d)
ERROR: column "phone" does not exist
LINE 3: cross join jsonb_array_elements(phone) as x(d)
another variant I tried
select d ->> 'value' as val
from analyst.person as person
cross join jsonb_array_elements(person.phone) as x(d)
gives error
ERROR: invalid reference to FROM-clause entry for table "person"
LINE 3: cross join jsonb_array_elements(person.phone) as x(d)
^
HINT: There is an entry for table "person", but it cannot be referenced from this part of the query.
You need to unnest your json array, extract the value and aggregate it back into a string:
select string_agg(val, '; ')
from (
select d ->> 'value' as val
from the_table
cross join lateral jsonb_array_elements(the_json_colum) as x(d)
) t
Online example: https://rextester.com/VFSRY99127