postgres jsonb update key value in array - sql

I have a table with a jsonb column with data from one row like
[
{
"a": [],
"c_id": 624,
"ps": [{"": 0, "pr": "73", "f": "M", "s": "M"}],
"g_n": "K L Mish",
"g_num": 1
},
{
"a": [],
"c_id": 719,
"ps": [{"": 0, "pr": "65433", "f": "R", "s": "W"}],
"g_n": "S H Star",
"g_num": 2
},
]
I want to update c_id in the table wherever it is 719 to 720.
How can I do it?
I am using Postgres 12.1

If it is only one single occurrence, you could do it using a Regular Expression:
Click: demo:db<>fiddle
UPDATE mytable
SET mydata = s.result::jsonb
FROM (
SELECT
regexp_replace(mydata::text, '(.*)("c_id"\s*:\s*)(719)(.*)','\1\2720\4') AS result
FROM
mytable
) s;
RegExp Groups:
(.*) All characters before the relevant key
("c_id"\s*:\s*) The relevant key incl. possible spaces
(719) The relevant value to be replaced
(.*) Everything after the relevant point
With \1\2720\4 you put the first two groups together, followed by the new value (instead of group 3) and the fourth group.
Disclaimer:
I fully agree with #a_horse_with_no_name: You should think about storing all values in separate and normalized tables/columns. You would gain a lot of benefits (much better search and update handling, indexing, performance, ...). If you need this JSON output, just handle it as output: Generate it when you need it, do not store it. Maybe a view could help a lot

Related

Postgres jsonb_array_elements() returns "cannot extract elements from a scalar", even when given a valid JSON array?

I have a table in postgres called day, which contains a jsonb column called plan_activities.
I have a day record with day.id = 18 and plan_activities contains the following JSON:
[
{
"activity": "Gym",
"cardio": false,
"strength": true,
"quantity": 20,
"units": "mins",
"timeOfDay": "Evening",
"summary": "Gym - 20 mins - Evening",
"timeOfDayOrder": 4
},
{
"activity": "Walk",
"cardio": true,
"strength": false,
"quantity": 15,
"units": "minutes",
"timeOfDay": "morning",
"summary": "Walk - 15 minutes - Lunchtime",
"timeOfDayOrder": 1
}
]
When I execute the following query:
select jsonb_array_elements(day.plan_activities) as activities
from day
where day.id = 18;
I get the following error:
Failed to run sql query: cannot extract elements from a scalar
The JSON contains a valid JSON array as far as I can tell. What am I doing wrong?
My eventual goal if I can extract this list is to create separate records elsewhere, each of which contains all the fields plus a reference back to the day record.
This error happens when you try to treat a JSON scalar, like a single string or number, as an array.
-- ERROR: cannot extract elements from a scalar
select jsonb_array_elements('23'::jsonb);
One of the rows of your query does not contain a JSON array.
Check with select plan_activities from day where id = 18. Although id is normally a unique primary key and it should be impossible to have more than one row returned.
Another way this could happen is if the JSON structure was accidentally added as a single JSON string.
-- 1, 2, 3
select jsonb_array_elements('[1, 2, 3]'::jsonb);
-- Note the extra quotes.
-- ERROR: cannot extract elements from a scalar
select jsonb_array_elements('"[1, 2, 3]"'::jsonb);

PostgreSQL JSON - String_agg in json data with multiple objects

I have create table with Json datatype field in PostgreSQL.
Also i have inserted data with multiple object in json data
like this
[
{
"Type": "1",
"Amount": "1000",
"Occurrence": 2,
"StartDate": "1990-01-19",
"EndDate": "1999-04-03"
},
{
"Type": "2",
"Amount": "2000",
"Occurrence": 2,
"StartDate": "1984-11-19",
"EndDate": "1997-09-29"
}
]
Now i have to retrieve my data as per below formate in single row like string_agg() function output.
Type Amount
1--2 1000-2000
also i have checked inbuilt function for json in PostgreSQL (https://www.postgresqltutorial.com/postgresql-json/) but not find any solutions for the same.
You will have to unnest the array and aggregate the individual keys:
The following assumes you have some kind of primary key column on the table (in addition to your JSON column):
select t.id,
string_agg(x.element ->> 'Type', '-') as types,
string_agg(x.element ->> 'Amount', '-') as amounts
from the_table t
cross join jsonb_array_elements(t.data) as x(element)
group by t.id;
jsonb_array_elements() extracts each array element and the main query then aggregates that back per ID using string_agg().
The above assumes your column is defined with the type jsonb (which it should be). If it is not, you need to use json_array_elements() instead.
Online example

Query data inside an attribute array in a json column in Postgres 9.6

I have a table say types, which had a JSON column, say location that looks like this:
{ "attribute":[
{
"type": "state",
"value": "CA"
},
{
"type": "distance",
"value": "200.00"
} ...
]
}
Each row in the table has the data, and all have the "type": "state" in it. I want to just extract the value of "type": "state" from every row in the table, and put it in a new column. I checked out several questions on SO, like:
Query for element of array in JSON column
Index for finding an element in a JSON array
Query for array elements inside JSON type
but could not get it working. I do not need to query on this. I need the value of this column. I apologize in advance if I missed something.
create table t(data json);
insert into t values('{"attribute":[{"type": "state","value": "CA"},{"type": "distance","value": "200.00"}]}'::json);
select elem->>'value' as state
from t, json_array_elements(t.data->'attribute') elem
where elem->>'type' = 'state';
| state |
| :---- |
| CA |
dbfiddle here
I mainly use Redshift where there is a built-in function to do this. So on the off-chance you're there, check it out.
redshift docs
It looks like Postgres has a similar function set:
https://www.postgresql.org/docs/current/static/functions-json.html
I think you'll need to chain three functions together to make this work.
SELECT
your_field::json->'attribute'->0->'value'
FROM
your_table
What I'm trying is a json extract by key name, followed by a json array extract by index (always the 1st, if your example is consistent with the full data), followed finally by another extract by key name.
Edit: got it working for your example
SELECT
'{ "attribute":[
{
"type": "state",
"value": "CA"
},
{
"type": "distance",
"value": "200.00"
}
]
}'::json->'attribute'->0->'value'
Returns "CA"
2nd edit: nested querying
#McNets is the right, better answer. But in this dive, I discovered you can nest queries in Postgres! How frickin' cool!
I stored the json as a text field in a dummy table and successfully ran this:
SELECT
(SELECT value FROM json_to_recordset(
my_column::json->'attribute') as x(type text, value text)
WHERE
type = 'state'
)
FROM dummy_table

Get JSON_VALUE with Oracle SQL when multiple nodes share the same name

I have an issue where I have some JSON stored in my oracle database, and I need to extract values from it.
The problem is, there are some fields that are duplicated.
When I try this, it works as there is only one firstname key in the options array:
SELECT
JSON_VALUE('{"increment_id":"2500000043","item_id":"845768","options":[{"firstname":"Kevin"},{"lastname":"Test"}]}', '$.options.firstname') AS value
FROM DUAL;
Which returns 'Kevin'.
However, when there are two values for the firstname field:
SELECT JSON_VALUE('{"increment_id":"2500000043","item_id":"845768","options":[{"firstname":"Kevin"},{"firstname":"Okay"},{"lastname":"Test"}]}', '$.options.firstname') AS value
FROM DUAL;
It only returns NULL.
Is there any way to select the first occurence of 'firstname' in this context?
JSON_VALUE returns one SQL VALUE from the JSON data (or SQL NULL if the key does not exists).
If you have a collection of values (a JSON array) an you want one specific item of the array you use array subscripts (square brackets) like in JavaScript, for example [2] to select the third item. [0] selects the first item.
To get the first array item in your example you have to change the path expression from '$.options.firstname' to '$.options[0].firstname'
You can follow this query:-
SELECT JSON_VALUE('{
"increment_id": "2500000043",
"item_id": "845768",
"options": [
{
"firstname": "Kevin"
},
{
"firstname": "Okay"
},
{
"lastname": "Test"
}
]
}', '$.options[0].firstname') AS value
FROM DUAL;

Query and count on jsonb column

I'm new to the postgreSQL(9.5) Json world. Looking for help writing this query. Take this simplified table as an example.
CREATE TABLE activity_log (uri varchar,
data jsonb );
Example of data inside of 'data' column.
"{"ListingInputFilterBean":{"searchItems": [], "listingStatus": "ACTIVE"}"
"{"ListingInputFilterBean":{"searchItems": [{"name": "Dachshund", "type": "BREED"}], "listingStatus": "ACTIVE"}}"
"{"ListingInputFilterBean":{"searchItems": [{"name": "Lab", "type": "BREED"}, {"name": "Black Lab", "type": "CST"}], "listingStatus": "ACTIVE"}}"
The 'data' column is used to log specific sets of data for each URI call. In this case the searchItems array contain the items used in the search. I'm looking to write a query that finds the most searched for 'breed'. I'd like to count the number of times each 'name' is used when type is 'BREED'.
My initial approach was to pull back each of the 'searchItems'. Turn those into a row set using jsonb_to_recordset, but I quickly got in over my head when reading the documentation (sorry, I'm a noob).
Any suggestions on how to write that SQL?
WITH log_activity(data) AS ( VALUES
('{"ListingInputFilterBean":{"searchItems": [], "listingStatus": "ACTIVE"}}'::JSONB),
('{"ListingInputFilterBean":{"searchItems": [{"name": "Dachshund", "type": "BREED"}], "listingStatus": "ACTIVE"}}'::JSONB),
('{"ListingInputFilterBean":{"searchItems": [{"name": "Lab", "type": "BREED"}, {"name": "Black Lab", "type": "CST"}], "listingStatus": "ACTIVE"}}'::JSONB)
)
SELECT search_item->>'name',count(search_item->>'name')
FROM
log_activity la,
jsonb_array_elements(la.data#>'{ListingInputFilterBean,searchItems}') as search_item
WHERE search_item->>'type' = 'BREED'
GROUP BY search_item;
Result:
name | count
-----------+-------
Lab | 1
Dachshund | 1
(2 rows)
Here you just need to iterate over the list of searchItems and group only those entries, which do match your criteria. Steps are the following:
Get jsonb array of searchItems with #> operator, it will get JSON object at specified path;
Iterate over the list of elements retrieved from step 1 with jsonb_array_elements(), function which expands a JSON array to a set of JSON values;
count() names where searchItems' type = BREED, you can get actual text value with ->> operator;
UPDATE
With jsonb_to_recordset() it looks shorter, but you need explicitly define search_item columns' types:
SELECT search_item.name ,count(search_item.name)
FROM
log_activity la,
jsonb_to_recordset(la.data#>'{ListingInputFilterBean,searchItems}') as search_item(name text,type text)
WHERE search_item.type = 'BREED'
GROUP BY search_item.name;