Equivalent function in HANA DB for json_object - sql

I would like to return the query results into json format in HANA DB.
There is a json_object function in oracle to achieve this requirement, but I am not seeing any function in HANA.
Does anyone knows if this kind of function exists in HANA
For example:
Table Author contains non-json data as follows:
---------------------------------------------
| firstName | lastName |
---------------------------------------------
| Paulo | Coelho |
| George | Orwell |
---------------------------------------------
write a select statement to return result as json.
In Oracle it can be returned using query:
SELECT json_object(
KEY 'firstName' VALUE author.first_name,
KEY 'lastName' VALUE author.last_name
)
FROM author
Output looks like this:
---------------------------------------------
| json_array |
---------------------------------------------
| {"firstName":"Paulo","lastName":"Coelho"} |
| {"firstName":"George","lastName":"Orwell"} |
----------------------------------------------
Does anyone knows query or function in HANA to achieve the same result?

you can use the already mentioned function in SAP HANA too
JSON_QUERY (
<JSON_API_common_syntax>
[ <JSON_output_clause> ]
[ <JSON_query_wrapper_behavior> ]
[ <JSON_query_empty_behavior> ON EMPTY ]
[ <JSON_query_error_behavior> ON ERROR ]
)
research

For 2.0 SP04 and above there's a for json addition to the select statement. As documentation says, it is only permitted in subqueries, so you need to select individual columns in subselect (if you need a result set of JSON objects) of generate a JSON array as a single scalar result. Column names are inherited from subquery aliases.
Case 1:
with a as (
select 'AAA' as field1, 'Value 1' as val from dummy union all
select 'BBB' as field1, 'Value 2' as val from dummy
)
select
/*Use correlated subquery with single row*/
json_value((select a.field1, a.val from dummy for json), '$[0]') as res
from a
Or more effort to type-in, but less structure-dependent:
with a as (
select 'AAA' as field1, 'Value 1' as val from dummy union all
select 'BBB' as field1, 'Value 2' as val from dummy
)
, json_source as (
/*Intermediate query to use as correlation source in JSON_TABLE*/
select (select * from a for json) as tmp_json
from dummy
)
select json_parsed.*
from json_source,
json_table(
json_source.tmp_json
/*Access individual items*/
, '$[*]'
columns (
res nvarchar(1000) format json path '$'
)
) as json_parsed
Both return:
RES
{"FIELD1":"AAA","VAL":"Value 1"}
{"FIELD1":"BBB","VAL":"Value 2"}
Or as a scalar query returning JSON array (Case 2):
with a as (
select 'AAA' as field1, 'Value 1' as val from dummy union all
select 'BBB' as field1, 'Value 2' as val from dummy
)
select *
from (select * from a for json)
JSONRESULT
[{"FIELD1":"AAA","VAL":"Value 1"},{"FIELD1":"BBB","VAL":"Value 2"}]

Related

Convert struct values to row in big query

I want to convert values of struct to independent row
My table looks like
|id | details
| 1 | {d_0:{id:'1_0'},d_1:{id:'1_1'}}
| 2 | {d_0:{id:'2_0'},d_1:{id:'2_1'}}
Expected Result (will be flattening the inner struct here)
| id |
|'1_0'|
|'1_1'|
|'2_0'|
|'2_1'|
Since IDK how many fields will be there in details is there any way to convert all the individual fields of the struct as independent rows.
The schema for all values in the details.d_0, details.d_1,... will be the same.
Any help or pointer to resources is appreciated.
You may use this query that iterates array to achieve your desired output:
Creating table:
CREATE TABLE `<proj_id>.<dataset>.<table>` as
WITH data AS (
SELECT "1" AS id, STRUCT(STRUCT( '1_0' as id) as d_0, STRUCT( '1_1' as id) as d_1) as details,
union all SELECT "2" AS id, STRUCT(STRUCT( '2_0' as id) as d_0, STRUCT( '2_1' as id) as d_1) as details
),
tier_1 as (
select id,details.* from data
)
select * from tier_1
Actual Query:
DECLARE i INT64 DEFAULT 0;
DECLARE query_ary ARRAY<STRING> DEFAULT
ARRAY(
select concat(column_name,'.id') from `<dataset>.INFORMATION_SCHEMA.COLUMNS`
WHERE
table_name = <your-table> AND regexp_contains(column_name, r'd\_\d')
);
CREATE TEMP TABLE result(id STRING);
LOOP
SET i = i + 1;
IF i > ARRAY_LENGTH(query_ary) THEN
LEAVE;
END IF;
EXECUTE IMMEDIATE '''
INSERT result
SELECT ''' || query_ary[ORDINAL(i)] || ''' FROM `<proj_id>.<dataset>.<table>`
''';
END LOOP;
SELECT * FROM result;
Output:
Consider below approach
select id from your_table,
unnest(split(translate(format('%t', details), '()', ''), ', ')) id
if applied to sample data in your question as
with your_table as (
select "1" id, struct(struct('1_0' as id) as d_0, struct('1_1' as id) as d_1) details union all
select "2", struct(struct('2_0'), struct('2_1'))
)
output is

Oracle regex to extract string between first pair of < and > brackets

I am have been assigned a task to parse a string (which is essentially in XML format) and I need to extract the name of the first tag in the string
eg: string '<column><data-type>string</data-type>.............'
or '<filter><condition>....</condition>...............'
or
'......................'
the string keeps changing but I am only interested in the first tag, I would like to get the output like:
column,
filter,
query
i have tried regexp_substr(string,'^<(.+)>',1,1,null,1) and some similer variations but they don't seem to be working cosistently.
Please help.
If you have XML data then use a proper XML parser:
SELECT XMLQUERY( '/*/name()' PASSING XMLTYPE(value) RETURNING CONTENT ) AS tag_name
FROM table_name
Which for the sample data:
CREATE TABLE table_name ( value CLOB );
INSERT INTO table_name ( value )
SELECT '<column><data-type>string</data-type></column>' FROM DUAL UNION ALL
SELECT '<filter><condition>....</condition></filter>' FROM DUAL UNION ALL
SELECT '<query />' FROM DUAL UNION ALL
SELECT '<has_attributes attr1="do not return this" attr2="<or> this" />' FROM DUAL
Outputs:
| TAG_NAME |
| :------------- |
| column |
| filter |
| query |
| has_attributes |
db<>fiddle here
You are looking for any character between the bounds -- and that includes '>'. So, just exclude the terminating character:
select regexp_substr(string,'^<([^>]+)>',1,1,null,1)
from (select '<column><data-type>string</data-type>.............' as string from dual union all
select '<filter><condition>....</condition>...............' from dual
) x;

Is there any way in MariaDB to search for less than value from array of json objects

Here's my json doc:
[
{
"ID":1,
"Label":"Price",
"Value":399
},
{
"ID":2,
"Label":"Company",
"Value":"Apple"
},
{
"ID":2,
"Label":"Model",
"Value":"iPhone SE"
},
]
Here's my table:
+----+------------------------------------------------------------------------------------------------------------------------------------+
| ID | Properties |
+----+------------------------------------------------------------------------------------------------------------------------------------+
| 1 | [{"ID":1,"Label":"Price","Value":399},{"ID":2,"Label":"Company","Value":"Apple"},{"ID":3,"Label":"Model","Value":"iPhone SE"}] |
| 2 | [{"ID":1,"Label":"Price","Value":499},{"ID":2,"Label":"Company","Value":"Apple"},{"ID":3,"Label":"Model","Value":"iPhone X"}] |
| 3 | [{"ID":1,"Label":"Price","Value":699},{"ID":2,"Label":"Company","Value":"Apple"},{"ID":3,"Label":"Model","Value":"iPhone 11"}] |
| 4 | [{"ID":1,"Label":"Price","Value":999},{"ID":2,"Label":"Company","Value":"Apple"},{"ID":3,"Label":"Model","Value":"iPhone 11 Pro"}] |
+----+------------------------------------------------------------------------------------------------------------------------------------+
Here's what I want to search on search query:
SELECT *
FROM mobiles
WHERE ($.Label = "Price" AND $.Value < 400)
AND ($.Label = "Model" AND $.Value = "iPhone SE")
Above mentioned query is just for illustration purpose only. I just wanted to convey what I want to perform.
Also I know the table can be normalized into two. But this table is also a place holder table and let's just say it is going to stay the same.
I need to know if it's possible to query the given json structure for following operators: >, >=, <, <=, BETWEEN AND, IN, NOT IN, LIKE, NOT LIKE, <>
Since MariaDB does not support JSON_TABLE(), and JSON_PATH supports only member/object selector, it is not so straightforward to filter JSON here. You can try this query, that tries to overcome that limitations:
with a as (
select 1 as id, '[{"ID":1,"Label":"Price","Value":399},{"ID":2,"Label":"Company","Value":"Apple"},{"ID":3,"Label":"Model","Value":"iPhone SE"}]' as properties union all
select 2 as id, '[{"ID":1,"Label":"Price","Value":499},{"ID":2,"Label":"Company","Value":"Apple"},{"ID":3,"Label":"Model","Value":"iPhone X"}]' as properties union all
select 3 as id, '[{"ID":1,"Label":"Price","Value":699},{"ID":2,"Label":"Company","Value":"Apple"},{"ID":3,"Label":"Model","Value":"iPhone 11"}]' as properties union all
select 4 as id, '[{"ID":1,"Label":"Price","Value":999},{"ID":2,"Label":"Company","Value":"Apple"},{"ID":3,"Label":"Model","Value":"iPhone 11 Pro"}]' as properties
)
select *
from a
where json_value(a.properties,
/*Get path to Price property and replace property name to Value*/
replace(replace(json_search(a.properties, 'one', 'Price'), '"', ''), 'Label', 'Value')
) < 400
and json_value(a.properties,
/*And the same for Model name*/
replace(replace(json_search(a.properties, 'one', 'Model'), '"', ''), 'Label', 'Value')
) = "iPhone SE"
| id | properties
+----+------------
| 1 | [{"ID":1,"Label":"Price","Value":399},{"ID":2,"Label":"Company","Value":"Apple"},{"ID":3,"Label":"Model","Value":"iPhone SE"}]
db<>fiddle here.
I would not use string functions. What is missing in MariaDB is the ability to unnest the array to rows - but it has all the JSON accessors we need to access to the data. Using these methods rather than string methods avoids edge cases, for example when the values contain embedded double quotes.
You would typically unnest the array with the help of a table of numbers that has at least as many rows as there are elements in the biggest array. One method to generate that on the fly is row_number() against a table with sufficient rows - say sometable.
You can unnest the arrays as follows:
select t.id,
json_unquote(json_extract(t.properties, concat('$[', n.rn, '].Label'))) as label,
json_unquote(json_extract(t.properties, concat('$[', n.rn, '].Value'))) as value
from mytable t
inner join (select row_number() over() - 1 as rn from sometable) n
on n.rn < json_length(t.properties)
The rest is just aggregation:
select t.id
from (
select t.id,
json_unquote(json_extract(t.properties, concat('$[', n.rn, '].Label'))) as label,
json_unquote(json_extract(t.properties, concat('$[', n.rn, '].Value'))) as value
from mytable t
inner join (select row_number() over() - 1 as rn from sometable) n
on n.rn < json_length(t.properties)
) t
group by id
having
max(label = 'Price' and value + 0 < 400) = 1
and max(label = 'Model' and value = 'iPhone SE') = 1
Demo on DB Fiddle

How to parse JSON in Standard SQL BigQuery?

After streaming some json data into BQ, we have a record that looks like:
"{\"Type\": \"Some_type\", \"Identification\": {\"Name\": \"First Last\"}}"
How would I extract the type from this? E.g. I would like to get Some_type.
I tried all possible combinations shown in https://cloud.google.com/bigquery/docs/reference/standard-sql/json_functions without success, namely, I thought:
SELECT JSON_EXTRACT_SCALAR(raw_json , "$[\"Type\"]") as parsed_type FROM `table` LIMIT 1000
is what I need. However, I get:
Invalid token in JSONPath at: ["Type"]
Picture of rows preview
Below example is for BigQuery Standard SQL
#standardSQL
WITH `project.dataset.table` AS (
SELECT 1 id, "{\"Type\": \"Some_type\", \"Identification\": {\"Name\": \"First Last\"}}" raw_json UNION ALL
SELECT 2, '{"Type": "Some_type", "Identification": {"Name": "First Last"}}'
)
SELECT id, JSON_EXTRACT_SCALAR(raw_json , "$.Type") AS parsed_type
FROM `project.dataset.table`
with result
Row id parsed_type
1 1 Some_type
2 2 Some_type
See below update example - take a look at third record which I think mimic your case
#standardSQL
WITH `project.dataset.table` AS (
SELECT 1 id, "{\"Type\": \"Some_type\", \"Identification\": {\"Name\": \"First Last\"}}" raw_json UNION ALL
SELECT 2, '''{"Type": "Some_type", "Identification": {"Name": "First Last"}}''' UNION ALL
SELECT 3, '''"{\"Type\": \"
null1\"}"
'''
)
SELECT id,
JSON_EXTRACT_SCALAR(REGEXP_REPLACE(raw_json, r'^"|"$', '') , "$.Type") AS parsed_type
FROM `project.dataset.table`
with result
Row id parsed_type
1 1 Some_type
2 2 Some_type
3 3 null1
Note: I use null1 instead of null so you can easily see that it is not a NULL but rather string null1

From a string array, create a table

Basically, when given a list of strings,
I want to create a table with select statement.
For example,
"A", "B", "C",
I want to create a table as a sub-select like:
sub-select
+---------+
| "A" |
+---------+
| "B" |
+---------+
| "C" |
+---------+
How do I do this in redshift and postgres?
Thanks!
Update:
select 'a' as A;
is sort of what I want that returns:
a
+---------+
| "a" |
+---------+
How do I have multiple rows for this column a from the query select 'a' as A;
One of the way to convert a column value to multiple rows is using split_part function and UNION.
Here is an example.
Source Table:
=> CREATE TABLE t_csv (value varchar(64));
=> INSERT INTO t_csv VALUES ('"A","B","C"');
=> INSERT INTO t_csv VALUES ('"D","E"');
=> INSERT INTO t_csv VALUES ('"F","G","H","I"');
=> SELECT * FROM t_csv;
value
-----------------
"D","E"
"A","B","C"
"F","G","H","I"
(3 rows)
Here is the query to get multiple rows.
=> WITH a AS (SELECT value FROM t_csv)
SELECT * FROM
(
SELECT split_part(a.value,',',1) AS value FROM a
UNION
SELECT split_part(a.value,',',2) AS value FROM a
UNION
SELECT split_part(a.value,',',3) AS value FROM a
UNION
SELECT split_part(a.value,',',4) AS value FROM a
)
WHERE value != '';
value
-------
"A"
"B"
"C"
"D"
"E"
"F"
"G"
"H"
"I"
(9 rows)
Have no chance to test it in db, but something like that
select * INTO table from (
SELECT CAST('A' AS VARCHAR(100)) AS col
UNION ALL
SELECT 'B' AS col
UNION ALL
SELECT 'C' AS col
) a
You can use string_to_array and unnest
select *
from unnest(string_to_array('"A","B","C"', ','))
(But I don't know if that is available in Redshift)