Redshift - SQL script to extract value from a key value pair - json

I have a columns containing JSON data as below. I am trying to extract values corresponding to each key pair in the column. Could anyone advice how could I do using SQL
[{"id": 101, "id1": {"key": "SaleId", "type": "identifier", "regex": null}, "id2": {"key": Name, "type": "identifier", "regex": null}, "id3": {"key": null, "type": "identifier", "regex": null}}]
Key values are id1, id2, id3
Expected output:
id1 : SaleId
id2 : Name
id3 : null
I am using Redshift. Thanks

I don't know anything about Redshift, so this might not Work.
It Works in JavaScript:
/"(id\d)":\s\{"key": "?(\w+)"?/g
You will then have to extract Group 1, containing the id and Group 2, containing the key.
The regex starts by matching a double quote, then creating a Group with the Word 'id' followed by a digit, a colon, a Space, a left curly brace, a double quote, the Word 'key', a colon, a Space, an optional double quote. Finally it creates a Group with one or more Word characters, followed by an optional double quote.
As I said, I don't know Redshift, for instance, you might have to escape double quotes.

You can do what you need like this
with t as
(
select '[{"id": 101, ' ||
'"id1": {"key": "SaleId", "type": "identifier", "regex": "null"}, ' ||
'"id2": {"key": "Name", "type": "identifier", "regex": "null"}, ' ||
'"id3": {"key": "null", "type": "identifier", "regex": "null"}}]' as str
)
select 'id1:' || json_extract_path_text(substring(str,2,length(str)-2),'id1','key'),
'id2:' || json_extract_path_text(substring(str,2,length(str)-2),'id2','key'),
'id3:' || json_extract_path_text(substring(str,2,length(str)-2),'id3','key')
from t;

The JSON string in your example is invalid because Name is not in double quotes.
Assuming this is a typo and this is meant to be a valid JSON string, then you can use JSON functions to extract the values you need from the column.
Example (I have added quotes around "Name"):
create temp table jsontest (myjsonstring varchar(1000))
;
insert into jsontest(myjsonstring)
values ('[{"id": 101, "id1": {"key": "SaleId", "type": "identifier", "regex": null}, "id2": {"key": "Name", "type": "identifier", "regex": null}, "id3": {"key": null, "type": "identifier", "regex": null}}]')
;
select 'id1', json_extract_path_text(json_extract_array_element_text(myjsonstring, 0) , 'id1', 'key') from jsontest
union all
select 'id2', json_extract_path_text(json_extract_array_element_text(myjsonstring, 0) , 'id2', 'key') from jsontest
union all
select 'id3', json_extract_path_text(json_extract_array_element_text(myjsonstring, 0) , 'id3', 'key') from jsontest
;

Related

Format output as JSON object instead of array

Table:
sqlite> select * from t;
id name date
-- ---- ----
1 ... ...
2 ... ...
3 ... ...
Schema:
sqlite> .schema t
CREATE TABLE t (id integer primary key, name text, date text);
The output can be formatted in JSON as an array of objects:
sqlite> .mode json
sqlite> select * from t;
[
{"id": 1, "name": "...", "date": "..."},
{"id": 2, "name": "...", "date": "..."},
{"id": 3, "name": "...", "date": "..."}
]
However, I would like an object where the keys are integers and the values are objects:
{
1: {"name": "...", "date": "..."},
2: {"name": "...", "date": "..."},
3: {"name": "...", "date": "..."}
}
Is this possible with SQLite?
Use function json_object() to get a json object for the name and date of each row and json_group_object() to aggregate all rows:
SELECT json_group_object(
id,
json_object('name', name, 'date', date)
) AS result
FROM t;
See the demo.

How to delete multiple values in JSONB array Postgresql array object

I have a JSONB array below
[
{
"name": "test",
"age": "21",
"phone": "6589",
"town": "54"
},
{
"name": "test12",
"age": "67",
"phone": "6546",
"town": "54"
},
{
"name": "test123",
"age": "21",
"phone": "6589",
"town": "54"
},
{
"name": "test125",
"age": "67",
"phone": "6546",
"town": "54"
}
]
Now I want to delete the object if the name is test or test125. How to delete multiple or single values in JSONB array?
An update statement including a subquery, which eleminates the unwanted elements with NOT IN operator and aggregates the rest by using jsonb_agg() function, would find out this operation :
Choose this :
1. UPDATE tab
SET jsdata = t.js_new
FROM
(
SELECT jsonb_agg( (jsdata ->> ( idx-1 )::int)::jsonb ) AS js_new
FROM tab
CROSS JOIN jsonb_array_elements(jsdata)
WITH ORDINALITY arr(j,idx)
WHERE j->>'name' NOT IN ('test','test125')
) t
or this one :
2. WITH t AS (
SELECT jsonb_agg( (jsdata ->> ( idx-1 )::int)::jsonb ) AS js_new
FROM tab
CROSS JOIN jsonb_array_elements(jsdata)
WITH ORDINALITY arr(j,idx)
WHERE j->>'name' NOT IN ('test','test125')
)
UPDATE tab
SET jsdata = js_new
FROM t
Demo
If you have the Postgres 12, you can use jsonb_path_query_array function to filter the jsonb here is the sample for your question:
with t (j) as ( values ('[
{"name":"test","age":"21","phone":"6589","town":"54"},
{"name":"test12","age":"67","phone":"6546","town":"54"},
{"name":"test123","age":"21","phone":"6589","town":"54"},
{"name":"test125","age":"67","phone":"6546","town":"54"}
]'::jsonb) )
select jsonb_path_query_array(j,
'$[*] ? (#.name != "test" && #.name != "test125")')
from t;
more info on https://www.postgresql.org/docs/12/functions-json.html
I would create a function that does that:
create function remove_array_elements(p_data jsonb, p_key text, p_value text[])
returns jsonb
as
$$
select jsonb_agg(e order by idx)
from jsonb_array_elements(p_data) with ordinality as t(e,idx)
where t.e ->> p_key <> ALL (p_value) ;
$$
language sql
immutable;
Then you can use it like this:
update the_table
set the_column = remove_array_elements(the_column, 'name', array['test', 'test125'])
where id = ...;
Online example

How to query nested array of jsonb

I am working on a PostgreSQL 11 table with a column of nested and multiple jsonb objects
to simulate the issue: -
CREATE TABLE public.test
(
id integer NOT NULL DEFAULT nextval('test_id_seq'::regclass),
testcol jsonb
)
insert into test (testcol) values
('[{"type": {"value": 1, "displayName": "flag1"}, "value": "10"},
{"type": {"value": 2, "displayName": "flag2"}, "value": "20"},
{"type": {"value": 3, "displayName": "flag3"}, "value": "30"},
{"type": {"value": 4, "displayName": "flag4"}},
{"type": {"value": 4, "displayName": "flag4"}},
{"type": {"value": 6, "displayName": "flag6"}, "value": "40"}]');
I am trying to:
get outer value if type= specific value. e.g. get the value 30, if flag3 is in displayname.
count occurrence of flag4 in inner json
You could use json_to_recordset to parse it:
WITH cte AS (
SELECT test.id, sub."type"->'value' AS t_value, sub."type"->'displayName' AS t_name, value
FROM test
,LATERAL jsonb_to_recordset(testcol) sub("type" jsonb, "value" int)
)
SELECT *
FROM cte
-- WHERE ...
-- GROUP BY ...;
db<>fiddle demo

Manipulating the JSON column and split the values

I have the following text in one of my Postgres table as TEXT datatype:
[
{"type": "text", "values": ["General"], "valueType": "string", "fieldType": "text", "value": ["General"], "customFieldId": "ee", "name": "customer_group"},
{"type": "text", "values": ["Vienna"], "valueType": "string", "fieldType": "text", "value": ["Vienna"], "customFieldId": "eU", "name": "customer_city"},
{"type": "text", "values": ["Mario"], "valueType": "string", "fieldType": "text", "value": ["Mario"], "customFieldId": "eZ", "name": "first_name"},
{"type": "text", "values": ["2019-06-30"], "valueType": "date", "fieldType": "text", "value": ["2019-06-30"], "customFieldId": "ea", "name": "created_at_date"}
]
I need to split the values of this TEXT field to columns and rows. For that I have converted the TEXT column to JSON as below:
SELECT CAST( "customFieldValues" as JSON) "customFieldValues" FROM fr.contacts
But when I tried to manipulate this JSON value I'm getting NULL as result.
WITH CTE AS(SELECT CAST( "customFieldValues" as JSON) "customFieldValues" FROM fr.contacts
)
SELECT
"customFieldValues" ->>'customer_city' as dd
FROM CTE
Does anyone have any suggestions on this? How to get the column names and it's values in rows. I want to create a TABLE based on this data.
Any suggestions would be of great help.
below is the expected result,
customer_group customer_city first_name created_at_date
General Vienna Mario 2019-06-30
Disclaimer: It is still not clear:
Why is there one element values and one value? What is the difference?
Why are these elements arrays?
step-by-step demo:db<>fiddle
SELECT
MAX(value) FILTER (WHERE column_name = 'customer_group') AS customer_group,
MAX(value) FILTER (WHERE column_name = 'customer_city') AS customer_city,
MAX(value) FILTER (WHERE column_name = 'first_name') AS first_name,
MAX(value) FILTER (WHERE column_name = 'created_at_date') AS created_at_date
FROM (
SELECT
elems ->> 'name' AS column_name,
elems -> 'value' ->> 0 AS value,
data
FROM
mytable,
json_array_elements(data::json) elems
) s
GROUP BY data
Cast text to json with ::json
Expand the JSON array: One row for each element with json_array_elements()
Getting the value: -> 'value' gets the array, ->> 0 gets the text representation of the first array element (the only one here)
Getting the column: ->> 'name' gets the text representation of the column name
Classical pivot algorithm (turning rows to columns) with the FILTER clause.

Update JSON object MySQL

I want to update a JSON object in MySQL.
TABLE
id (int-11, not_null, auto_inc)
labels (json)
JSON Beautify
[
{
"tagname": "FOO",
"category": "CAT_1",
"isnew": "no",
"isdeleted": "no"
},
{
"tagname": "BAR",
"category": "CAT_2",
"isnew": "yes",
"isdeleted": "no"
}
]
I want to add a new TAG element (JSON OBJECT) next to the existing objects, but without SELECTing first the field and updating all the field with text.
I have Google a lot but I can not understand yet the MySQL's JSON treatment. I have just learned how to insert data like this:
INSERT INTO `table_name`(
`id` ,
`labels`
)
VALUES(
null ,
JSON_ARRAY
(
JSON_OBJECT
(
"tagname", "FOO",
"category", "CAT_1",
"isnew", "no",
"isdeleted", "no"
),
JSON_OBJECT
(
"tagname", "BAR",
"category", "CAT_2",
"isnew", "yes",
"isdeleted", "no"
)
)
);
You could use JSON_ARRAY_APPEND:
UPDATE tab
SET labels = JSON_ARRAY_APPEND(labels, '$',
JSON_OBJECT
(
"tagname", "BARX",
"category", "CAT_3",
"isnew", "yes",
"isdeleted", "no"
)
)
WHERE ID = 1;
DB-Fiddle.com demo