I want to merge JSON string in a table and sum its value after group
for Eg:-
2023, {"hen":4, "owl":3}
2023, {"crow":4, "owl":2}
2022, {"owl":6, "crow":2}
2022, {"hen":5}
2021, {"hen":2, "crow":1}
Result could be like below
2023, {"hen":4, "owl":5, "crow":4}
2022, {"hen":5, "owl":6, "crow":2}
2021, {"hen":2, "crow":1}
below might be an option when you don't know json object keys beforehand.
WITH sample_table AS (
SELECT 2023 year, '{"hen":4, "owl":3}' json UNION ALL
SELECT 2023, '{"crow":4, "owl":2}' UNION ALL
SELECT 2022, '{"owl":6, "crow":2}' UNION ALL
SELECT 2022, '{"hen":5}' UNION ALL
SELECT 2021, '{"hen":2, "crow":1}'
)
SELECT year, '{' || STRING_AGG(k || ':' || v, ', ') || '}' json FROM (
SELECT year,
SPLIT(kv, ':')[OFFSET(0)] k,
SUM(SAFE_CAST(SPLIT(kv, ':')[OFFSET(1)] AS INT64)) v
FROM sample_table, UNNEST(SPLIT(TRIM(json, '{}'), ', ')) kv
GROUP BY 1, 2
) GROUP BY 1;
Query results
Consider also below approach
create temp function get_keys(input string) returns array<string> language js as """
return Object.keys(JSON.parse(input));
""";
create temp function get_values(input string) returns array<string> language js as """
return Object.values(JSON.parse(input));
""";
select distinct year,
'{' || string_agg(format('"%s":%i', key ,sum(cast(val as int64))), ', ') over(partition by year) || '}' json
from your_table, unnest(get_keys(json)) key with offset
join unnest(get_values(json)) val with offset
using (offset)
group by year, key
if applied to sample data in your question - output is
What I'm trying to do is fill up a table with the data from a JSON. The file is formatted like this.
[
{
"name": "Victor",
"age": "20"
},
{
"name": "Ana",
"age": "23"
}
]
I can't change how it's formatted.
I tried using APEX_JSON to parse it and add row by row, but I can't even use the GET_COUNT, none of the paths I tried worked.
The database is an Oracle 11g, so there's no JSON_TABLE
--oracle 12c or later
SELECT *
FROM JSON_TABLE (
'[{"name":"Victor", "age":"20"},{"name":"Ana", "age":"23"}]',
'$[*]'
COLUMNS
NAME VARCHAR2 (2000) PATH '$.name',
AGE VARCHAR2 (2000) PATH '$.age')
--oracle 11g
SELECT *
FROM XMLTABLE (
'/json/row'
PASSING apex_json.to_xmltype (
'[{"name":"Victor", "age":"20"},{"name":"Ana", "age":"23"}]')
COLUMNS
NAME VARCHAR2 (2000) PATH '/row/name',
AGE VARCHAR2 (2000) PATH '/row/age')
You can use XMLTABLE along with APEX_JSON.TO_XMLTYPE() function in order to simulate JSON_TABLE such as
WITH t(jsCol) AS
(
SELECT '[
{
"name": "Victor",
"age": "20"
},
{
"name": "Anna",
"age": "23"
}
]'
FROM dual
)
SELECT name, age
FROM t,
XMLTABLE('/json/row'
PASSING APEX_JSON.TO_XMLTYPE(jsCol)
COLUMNS
name VARCHAR2(100) PATH 'name',
age VARCHAR2(100) PATH 'age'
)
NAME
AGE
Victor
20
Anna
23
With APEX_JSON you can do something like this:
DECLARE
l_json_text VARCHAR2(32767);
l_json_values apex_json.t_values;
BEGIN
l_json_text := '[
{"name":"Victor", "age":"20"},
{"name":"Ana", "age":"23"}
]
';
apex_json.parse(
p_values => l_json_values,
p_source => l_json_text
);
DBMS_OUTPUT.put_line('----------------------------------------');
FOR r IN 1 .. nvl(apex_json.get_count(p_path => '.', p_values => l_json_values),0) loop
dbms_output.put_line(apex_json.get_varchar2(p_path => '[%d].name', p0 => r, p_values => l_json_values));
dbms_output.put_line(apex_json.get_varchar2(p_path => '[%d].age', p0 => r, p_values => l_json_values));
/* insert into your_table
(name,
age
)
VALUES
(
apex_json.get_varchar2(p_path => '[%d].name', p0 => r, p_values => l_json_values),
apex_json.get_varchar2(p_path => '[%d].age', p0 => r, p_values => l_json_values)
);
*/
END loop;
DBMS_OUTPUT.put_line('----------------------------------------');
END;
/
If you can find a proper JSON parser then you should use that; however, if one is not available, you could parse it yourself. From Oracle 11gR2, you can use:
INSERT INTO table_name (name, age)
WITH jsondata (json) AS (
SELECT '[
{"name":"Victor", "age":"20"},
{"name":"Ana", "age":"23"},
{
"name":"Betty",
"age":"24"
},
{
"age":"25",
"name":"Carol"
}
]' FROM DUAL
),
data (json, items, i, name, age) AS (
SELECT json,
REGEXP_COUNT(
json,
'\{\s*"name"\s*:\s*"(.*?)"\s*,\s*"age"\s*:\s*"(.*?)"\s*\}'
|| '|\{\s*"age"\s*:\s*"(.*?)"\s*,\s*"name"\s*:\s*"(.*?)"\s*\}',
1,
'n'
),
1,
REGEXP_SUBSTR(
REGEXP_SUBSTR(
json,
'\{\s*"name"\s*:\s*"(.*?)"\s*,\s*"age"\s*:\s*"(.*?)"\s*\}'
|| '|\{\s*"age"\s*:\s*"(.*?)"\s*,\s*"name"\s*:\s*"(.*?)"\s*\}',
1,
1,
'n'
),
'"name"\s*:\s*"(.*?)"',
1,
1,
'n',
1
),
REGEXP_SUBSTR(
REGEXP_SUBSTR(
json,
'\{\s*"name"\s*:\s*"(.*?)"\s*,\s*"age"\s*:\s*"(.*?)"\s*\}'
|| '|\{\s*"age"\s*:\s*"(.*?)"\s*,\s*"name"\s*:\s*"(.*?)"\s*\}',
1,
1,
'n'
),
'"age"\s*:\s*"(.*?)"',
1,
1,
'n',
1
)
FROM jsondata
UNION ALL
SELECT json,
items,
i + 1,
REGEXP_SUBSTR(
REGEXP_SUBSTR(
json,
'\{\s*"name"\s*:\s*"(.*?)"\s*,\s*"age"\s*:\s*"(.*?)"\s*\}'
|| '|\{\s*"age"\s*:\s*"(.*?)"\s*,\s*"name"\s*:\s*"(.*?)"\s*\}',
1,
i + 1,
'n'
),
'"name"\s*:\s*"(.*?)"',
1,
1,
'n',
1
),
REGEXP_SUBSTR(
REGEXP_SUBSTR(
json,
'\{\s*"name"\s*:\s*"(.*?)"\s*,\s*"age"\s*:\s*"(.*?)"\s*\}'
|| '|\{\s*"age"\s*:\s*"(.*?)"\s*,\s*"name"\s*:\s*"(.*?)"\s*\}',
1,
i + 1,
'n'
),
'"age"\s*:\s*"(.*?)"',
1,
1,
'n',
1
)
FROM data
WHERE i < items
)
SELECT name, age
FROM data;
(Note: the regular expression does not handle escaped quotes in the strings as I am assuming they will not occur in names; however, if they do then instead of .*? you can use (\(["\/bfnrt]|u[0-9a-fA-F]{4})|[^"])*.)
Which, given the table:
CREATE TABLE table_name (name VARCHAR2(30), age NUMBER);
Then after the insert:
SELECT * FROM table_name;
Outputs:
NAME
AGE
Victor
20
Ana
23
Betty
24
Carol
25
db<>fiddle here
Last time done that with a clob variable.
Try to do it like :
DECLARE
json_body clob := '[
{"name":"Victor", "age":"20"},
{"name":"Ana", "age":"23"}
]';
BEGIN
FOR items IN (SELECT *
FROM
JSON_TABLE(json_body FORMAT JSON,'$[*]'
COLUMNS (
name_ varchar (200) PATH '$.name',
age_ varchar (200) PATH '$.age')))
LOOP
INSERT INTO T_DATA (
name,
age
) VALUES (
items.name_,
items.age_
);
END LOOP;
END;
/
This will put your data into a table and then you can play with them
select * from T_DATA;
Resulting into :
result
I have month value like "22018" in my column I need it like Feb-2018 in mysql workbench
You need to first extract the month from the date (considering it will have one or two digits), e.g.:
SELECT LPAD(SUBSTRING('22018', 1, LENGTH('22018') - 4), 2, '0');
This will give you 02. Now, you can extract the year with similar logic, e.g.:
SELECT SUBSTRING('22018', LENGTH('22018') - 4 + 1, LENGTH('22018'));
Finally, you can concatenate all these to get a string like 2018-02-01:
SELECT CONCAT(SUBSTRING('22018', LENGTH('22018') - 4 + 1, LENGTH('22018')),
'-',
LPAD(SUBSTRING('22018', 1, LENGTH('22018') - 4), 2, '0'), '-01');
Once this is done, you can use DATE_FORMAT function to get the required output:
SELECT DATE_FORMAT(CONCAT(SUBSTRING('22018', LENGTH('22018') - 4 + 1,
LENGTH('22018')),
'-',
LPAD(SUBSTRING('22018', 1, LENGTH('22018') - 4), 2, '0'), '-01'), '%M-%Y');
Is it possible to sort integers character by character?
So the list [110, 120, 10, 200, 20] would end up like this:
10,
110,
120,
20,
200
If you cast the integer as a varchar or text value, and order by that cast, it should work.
ORDER BY CAST(myField AS CHAR)
SELECT num
FROM your_table
ORDER BY CAST(num as CHAR)
I have a column with values like this:
01709100011
I need to transform it to:
017.091.0001-1
The values have always the same characters number.
Both columns are varchar
Thanks in advance for any help.
SELECT CONCAT(SUBSTRING(test, 1,3),'.',SUBSTRING(test,4,3),'.',SUBSTRING(test,7,4),'-',SUBSTRING(test,11,1)) FROM test;
In the above example I used the table test and values in column test.
SELECT CONCAT_WS( "-", CONCAT_WS( ".", SUBSTRING( foo, 0, 3 ), SUBSTRING( foo, 3, 3 ), SUBSTRING( 6, 4 )), SUBSTRING( foo, 10 , 1 )) FROM bar WHERE 1=1;