Extracting the elements of a JsonB dictionary in PostgreSQL 12 - json

I have looked at similar questions, but I can't find how to go from storing dictionaries in:
Table A: id int, data jsonb
For example:
id = 1
data = {"Key1": 1, "Key2": "a2", "Key3": [3, 4]}
to Table B: id int, key text, payload jsonb
Using the same example as above, I would get the 3 records:
id Key payload
--------------------
1 Key1 1
1 Key2 "a2"
1 Key3 [3, 4]
Thanks in advance for any help!

Use jsonb_each():
insert into table_b
select id, key, payload
from table_a
cross join lateral jsonb_each(data) as e(key, payload);

Related

Update JSONB with UUID value

I have a table with a column of jsonb type, the JSON object is of key:value pairs, the problem is, I need to update one of the keys to contain a UUID instead of the original value.
The update statement I'm using:
UPDATE
public.applications a
SET
data = jsonb_set(data, '{id}', (SELECT b.uuid FROM public.devices b WHERE b.id = (a.data ->> 'id')::integer)::text)
Postgres returns the following error:
ERROR: function jsonb_set(jsonb, unknown, text) does not exist
I've tried to cast the value to different data types, but with same result.
Sample data
id | data
---------
1 | {"id": 1}
2 | {"id": 2}
Expected output
id | data
---------
1 | {"id": device_uuid_here}
2 | {"id": device_uuid_here}
DB_VERSION: PostgreSQL12.12
UPDATE public.applications a
SET data = jsonb_set(data, '{id}',
(SELECT '"'||b.uuid::text||'"' FROM public.devices b
WHERE b.id = (a.data ->> 'id')::integer)::jsonb);

Postgres: transform json object into array of key-value pairs

With this select:
SELECT '{"key1": "value1", "key2": "value2"}'::jsonb
can we transform the result object into the following array of objects keeping their keys and values respectively?
[{"key1": "value1"}, {"key2": "value2"}]
In my case, I have many-2-many relationship of tags and operations, and the following query with aggregation of tags into json object for each operation:
SELECT ot.operation_id, json_object_agg(t.name, t.value) AS tagsjson
FROM tag t
INNER JOIN operation_tag ot ON t.tag_id = ot.tag_id
GROUP BY ot.operation_id
This works fine except format of the result: I need array of tags [{name : value},...] instead of whole object containing tags as it's properties:
If I understand you correctly you are in a situation like the following:
your source table contains id, keys and values like the below
create table test_k_v (id int, k varchar, v varchar);
insert into test_k_v values (1, 'key1', 'value1');
insert into test_k_v values (1, 'key2', 'value2');
insert into test_k_v values (2, 'key3', 'value3');
insert into test_k_v values (2, 'key4', 'value4');
insert into test_k_v values (2, 'key5', 'value5');
which results in
defaultdb=> select * from test_k_v;
id | k | v
----+------+--------
1 | key1 | value1
1 | key2 | value2
2 | key3 | value3
2 | key4 | value4
2 | key5 | value5
(5 rows)
If so, you can create an array with all the tags with the following query:
select id, array_agg(json_build_object(k, v)) agg_res
from test_k_v
group by id;
result
id | agg_res
----+---------------------------------------------------------------------------------
2 | {"{\"key3\" : \"value3\"}","{\"key4\" : \"value4\"}","{\"key5\" : \"value5\"}"}
1 | {"{\"key1\" : \"value1\"}","{\"key2\" : \"value2\"}"}
(2 rows)

MySQL Parse and Split JSON value

I have a column which contains a JSON value of different lengths
["The Cherries:2.50","Draw:3.25","Swansea Jacks:2.87"]
I want to split them and store into a JSON like so:
[
{
name: "The Cherries",
odds: 2.50
},
{
name: "Draw",
odds: 3.25
},
{
name: "Swansea",
odds: 2.87
},
]
What I did right now is looping and splitting them in the UI which to me is quite heavy for the client. I want to parse and split them all in a single query.
If you are running MySQL 8.0, you can use json_table() to split the original arrayto rows, and then build new objects and aggregate them with json_arrayagg().
We need a primary key column (or set of columns) so we can properly aggreate the generated rows, I assumed id:
select
t.id,
json_arrayagg(json_object(
'name', substring(j.val, 1, locate(':', j.val) - 1),
'odds', substring(j.val, locate(':', j.val) + 1)
)) new_js
from mytable t
cross join json_table(t.js, '$[*]' columns (val varchar(500) path '$')) as j
group by t.id
Demo on DB Fiddle
Sample data:
id | js
-: | :-------------------------------------------------------
1 | ["The Cherries:2.50", "Draw:3.25", "Swansea Jacks:2.87"]
Query results:
id | new_js
-: | :----------------------------------------------------------------------------------------------------------------------
1 | [{"name": "The Cherries", "odds": "2.50"}, {"name": "Draw", "odds": "3.25"}, {"name": "Swansea Jacks", "odds": "2.87"}]
You can use json_table to create rows from the json object.
Just replace table_name with your table name and json with the column that contains json
SELECT json_arrayagg(json_object('name',SUBSTRING_INDEX(person, ':', 1) ,'odds',SUBSTRING_INDEX(person, ':', -1) ))
FROM table_name,
JSON_TABLE(json, '$[*]' COLUMNS (person VARCHAR(40) PATH '$') people;
Here is a Db fiddle you can refer
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=801de9f067e89a48d45ef9a5bd2d094a

How to select JSON object from JSON array field of mysql by some condition

I have a table with JSON field which contains an array of JSON objects. I need to select objects by some condition.
Create and fill a table:
CREATE TABLE test (
id INT AUTO_INCREMENT PRIMARY KEY,
json_list JSON
);
INSERT INTO test(json_list) VALUES
("{""list"": [{""type"": ""color"", ""value"": ""red""}, {""type"": ""shape"", ""value"": ""oval""}, {""type"": ""color"", ""value"": ""green""}]}"),
("{""list"": [{""type"": ""shape"", ""value"": ""rect""}, {""type"": ""color"", ""value"": ""olive""}]}"),
("{""list"": [{""type"": ""color"", ""value"": ""red""}]}")
;
Now I need to select all objects with type = color from all rows.
I want to see this output:
id extracted_value
1 {"type": "color", "value": "red"}
1 {"type": "color", "value": "green"}
2 {"type": "color", "value": "olive"}
3 {"type": "color", "value": "red"}
It would be good to get this too:
id color
1 red
1 green
2 olive
3 red
I can't change the DB or JSON.
I'm using MySQL 5.7
My current solution
My solution is to cross join the table with some index set and then extract all elements of JSON array.
I don't like it as if possible object count in one array is large it is required to have all indexes till the maximum one. It makes the query slow as it won't stop calculation of JSON value when the end of array is reached.
SELECT
test.id,
JSON_EXTRACT(test.json_list, CONCAT('$.list[', ind.ind, ']')),
ind.ind
FROM
test
CROSS JOIN
(SELECT 0 AS ind UNION ALL SELECT 1 AS ind UNION ALL SELECT 2 AS ind) ind
WHERE
JSON_LENGTH(json_list, "$.list") > ind.ind
AND JSON_EXTRACT(json_list, CONCAT('$.list[', ind.ind, '].type')) = "color";
It is easy to get only values by changing JSON_EXTRACT path. But is it there a better way?
Edits
Added a check for json_list.list length. This filtered out 67% of derived table rows in this case.
SELECT JSON_EXTRACT(json_list, '$.list[*]')
FROM `test`
where JSON_CONTAINS(json_list, '{"type":"color"}', '$.list')
So current best solution is mine:
SELECT
test.id,
JSON_EXTRACT(test.json_list, CONCAT('$.list[', ind.ind, ']')),
ind.ind
FROM
test
CROSS JOIN
(SELECT 0 AS ind UNION ALL SELECT 1 AS ind UNION ALL SELECT 2 AS ind) ind
WHERE
JSON_LENGTH(json_list, "$.list") > ind.ind
AND JSON_EXTRACT(json_list, CONCAT('$.list[', ind.ind, '].type')) = "color";

Does Postgres allow json[] or jsonb[]?

So l have been trying to find an answer on the internet with 0 luck.
Does postgres support having arrays of objects in a single field, e.g.
[
{
key: value,
another: value
},
{
key: value,
value: key
}
]
and saving this to a single field?
Also how would you perform the single INSERT or UPDATE
would it be: UPDATE db SET value='[{ key: val }, { key: val }]' ??
Postgres supports any valid json values, including json arrays.
What you are going to use is a single json (jsonb) column, not a Postgres array:
create table example (id int, val jsonb);
insert into example
values (1, '[{ "name": "aga" }, { "gender": "female" }]');
select * from example;
id | val
----+-----------------------------------------
1 | [{"name": "aga"}, {"gender": "female"}]
(1 row)
It depends on your definitoion of objects I guess.
You can use JSON: http://www.postgresql.org/docs/current/static/functions-json.html and insert unstructured data:
# create table test (field json);
CREATE TABLE
# insert into test values ('[1,2,3]');
INSERT 0 1
# insert into test values ('[{"key": "value"}, {"key": "value"}]');
INSERT 0 1
# select * from test;
field
--------------------------------------
[1,2,3]
[{"key": "value"}, {"key": "value"}]
There is also support for arrays: http://www.postgresql.org/docs/current/static/arrays.html