Updating json columns in psql - postgresql

I have a table containing a json column. The json values will look something like this:
{'john': 1, 'alex' : 4, 'harry' :2}
If I wanted to add 1 to john, how would I go about doing this?

demo:db<>fiddle
UPDATE mytable -- 6
SET mydata = jsonb_set( -- 4
mydata::jsonb, -- 1
'{john}', -- 2
((mydata ->> 'john')::int + 1)::text::jsonb -- 3
)::json; -- 5
Fetch your data. If it is of type json, cast it into type jsonb
Path to your requested element as text array
Fetch the original value. ->> operator returns type text, so to do an integer operation, you need to cast it into type int. Then add the 1. This result must be reconverted into type jsonb. Unfortunately type int cannot be cast into type jsonb directly, so take the intermediate step via type text
Use jsonb_set() to update the JSON object specified in (1)
If your column is of type json instead of jsonb, cast the result back into type json
Perform the update

Related

How can I compare json field with a string value in postgresql?

I have a field payload saved in a postgresql table which is json type. This type has a nested field subcategory which is string. Below is the output of this value:
=> select payload->'subcategory' from "Merchant";
?column?
-------------------------------
"Food"
null
"AUTOMOTIVE"
null
"MEDICAL"
null
null
"CLUB"
"Petrol Stations"
However, I can't put this field in the where clause. Below query returns 0 rows. But from above output it shows there are rows whose value is CLUB. What is the right way to use json field in where clause?
=> select count(*) from "Merchant" where ("payload"->'subcategory')::text = 'CLUB';
count
-------
0
Figured out what's wrong, I need to use ->> in the where like "payload"->'subcategory'.
that because ->> converts it to text while -> makes it as JSONB
An alternative solution is to use the JSON contains operator #>:
select count(*)
from "Merchant"
where payload #> '{"subcategory": "CLUB")';

How can I convert a jsonb nested string field to int in postgresql?

I am using postgresql 11 and I have a jsonb field.
amount
----------------------------------------
{"value": "3590", "currency": "AUD"}
I can use this syntax to select the nested field value: select amount->'value'.
but I don't know how I can convert it to integer and sum it, like select sum(amount->'value') from
I got this error: HINT: No function matches the given name and argument types. You might need to add explicit type casts.
I tried to convert it by
select sum(amount->'value')::DECIMAL
select sum(TO_NUMBER(amount->'value'))
but none of them working. What is the correct way to do that?
Use the ->> operator to get the value as text:
https://www.postgresql.org/docs/11/functions-json.html
SELECT SUM((amount->>'value')::INT)

Build jsonb array from jsonb field

I have column options with type jsonb , in format {"names": ["name1", "name2"]} which was created with
UPDATE table1 t1 SET options = (SELECT jsonb_build_object('names', names) FROM table2 t2 WHERE t2.id= t1.id)
and where names have type jsonb array.
SELECT jsonb_typeof(names) FROM table2 give array
Now I want to extract value of names as jsonb array. But query
SELECT jsonb_build_array(options->>'names') FROM table
gave me ["[\"name1\", \"name2\"]"], while I expect ["name1", "name2"]
How can I get value in right format?
The ->> operator will return the value of the field (in your case, a JSON array) as a properly escaped text. What you are looking for is the -> operator instead.
However, note that using the jsonb_build_array on that will return an array containing your original array, which is probably not what you want either; simply using options->'names' should get you what you want.
Actually, you don't need to use jsonb_build_array() function.
Use select options -> 'names' from table; This will fix your issue.
jsonb_build_array() is for generating the array from jsonb object. You are following wrong way. That's why you are getting string like this ["[\"name1\", \"name2\"]"].
Try to execute this sample SQL script:
select j->'names'
from (
select '{"names": ["name1", "name2"]}'::JSONB as j
) as a;

How to split array in json using json_query?

I've got a column in a table that's a json. It contains only values without keys like
Now I'm trying to split the data from the json and create new table using every index of each array as new entry like
I've already tried
SELECT JSON_QUERY(abc) as 'Type', Id as 'ValueId' from Table FOR JSON AUTO
Is there any way to handle splitting given that some arrays might be empty and look like
[]
?
A fairly simply approach would be to use outer apply with openjson.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
Id int,
Value nvarchar(20)
)
INSERT INTO #T VALUES
(1, '[10]'),
(2, '[20, 200]'),
(3, '[]'),
(4, '')
The query:
SELECT Id, JsonValues.Value
FROM #T As t
OUTER APPLY
OPENJSON( Value ) As JsonValues
WHERE ISJSON(t.Value) = 1
Results:
Id Value
1 10
2 20
2 200
3 NULL
Note the ISJSON condition in the where clause will prevent exceptions in case the Value column contains anything other than a valid json (an empty array [] is still considered valid for this purpose).
If you don't want to return a row where the json array is empty, use cross apply instead of outer apply.
Your own code calling for FOR JSON AUTO tries to create JSON out of tabular data. But what you really needs seems to be the opposite direction: You want to transform JSON to a result set, a derived table. This is done by OPENJSON.
Your JSON seems to be a very minimalistic array.
You can try something along this.
DECLARE #json NVARCHAR(MAX) =N'[1,2,3]';
SELECT * FROM OPENJSON(#json);
The result returns the zero-based ordinal position in key, the actual value in value and a (very limited) type-enum.
Hint: If you want to use this against a table's column you must use APPLY, something along
SELECT *
FROM YourTable t
OUTER APPLY OPENJSON(t.TheJsonColumn);

Postgres: create index on attribute of attribute in JSONB column?

I'm working in Postgres 9.6.5. I have the following table:
id | integer
data | jsonb
The data in the data column is nested, in the form:
{ 'identification': { 'registration_number': 'foo' }}
I'd like to index registration_number, so I can query on it. I've tried this (based on this answer):
CREATE INDEX ON mytable((data->>'identification'->>'registration_number'));
But got this:
ERROR: operator does not exist: text ->> unknown
LINE 1: CREATE INDEX ON psc((data->>'identification'->>'registration... ^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
What am I doing wrong?
You want:
CREATE INDEX ON mytable((data -> 'identification' ->> 'registration_number'));
The -> operator returns the jsonb object under the key, and the ->> operator returns the jsonb object under the key as text. The most notable difference between the two operators is that ->> will "unwrap" string values (i.e. remove double quotes from the TEXT representation).
The error you're seeing is reported because data ->> 'identification' returns text, and the subsequent ->> is not defined for the text type.
Since version 9.3 Postgres has the #> and #>> operators. This operators allow the user to specify a path (using an array of text) inside jsonb column to get the value.
You could use this operator to achieve your goal in a simpler way.
CREATE INDEX ON mytable((data #>> '{identification, registration_number}'));