Postgres jsonb : cast array element to integer - postgresql

Using Postgres 11.2.9 (ubuntu),
In my database, I have a jsonb field containing values that look like this :
[1618171589133, 1618171589245, 1618171589689]
I'd like to retrieve rows where the first element is lower than a specific value. I've tried this :
SELECT * FROM user.times WHERE time ->> 0 < 1618171589133
but I get the following error : ERROR: operator does not exist: text = bigint
Should I somehow cast the time value to numeric value ? I've tried time ->> 0::numeric but I actually don't know what to do.

The ->> operator returns the element at given position as text, which you can then convert to integer (or as it seems in this case, bigint), as you would normally do in postgres, using the :: as suffix.
SELECT * FROM user.times WHERE ((time ->> 0)::bigint) < 1618171589133

Related

Cast top-level JSONB field as UUID

Consider a table like so:
CREATE TABLE items (
e uuid,
v jsonb
)
I insert the following values:
INSERT INTO items (e, v) VALUES
('9a70439e-33c0-4b34-91f5-efac20b58301', '"92cb730c-8b4f-46ef-9925-4fab953694c6"'),
('92cb730c-8b4f-46ef-9925-4fab953694c6', '"Bob"'),
('92cb730c-8b4f-46ef-9925-4fab953694c6', '52');
Note how for v, I have actually stringified text and numbers -- not an object.
Now, what if I wanted to write a query like so:
WITH match AS (
SELECT * FROM items WHERE e = '9a70439e-33c0-4b34-91f5-efac20b58301'
) SELECT * FROM items JOIN match ON match.v = items.e;
I would get:
Query Error: error: operator does not exist: jsonb = uuid
Is there a way I could tell postgres to "parse" the jsonb value, and see if it is a uuid?
I know about Postgres cast to UUID from JSON , where the ->> operator was able to do the trick. But I can't do that in this case, because our json value is a strong not an object.
You can use ->> 0 to extract the value as text:
SELECT * FROM items
WHERE e = '9a70439e-33c0-4b34-91f5-efac20b58301'
AND e::text = v ->> 0;

How can I get the length of a property in a jsonb in postgres?

product
---------------------------
{"id":1, "code": "1231313"}
this is the query i made, but it doesn't work
SELECT * FROM public.order WHERE "product" ->> LENGTH('code') = '7';
I would like to know how I can get the length of the value of this property from a jsonb, please help me, thanks.
Below is the way to use the ->> operator to extract field from a column of type JSON(B) and then apply length() to get the text length.
select * from public.order where length(product->>'code') >= 7
See the various operators/functions to get data from JSON(B) column type, Postgresql documentation

Updating json columns in psql

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

Update with ISNULL and operation

original query looks like this :
UPDATE reponse_question_finale t1, reponse_question_finale t2 SET
t1.nb_question_repondu = (9-(ISNULL(t1.valeur_question_4)+ISNULL(t1.valeur_question_6)+ISNULL(t1.valeur_question_7)+ISNULL(t1.valeur_question_9))) WHERE t1.APPLICATION = t2.APPLICATION;
I know you cannot update 2 tables in a single query so i tried this :
UPDATE reponse_question_finale t1
SET nb_question_repondu = (9-(COALESCE(t1.valeur_question_4,'')::int+COALESCE(t1.valeur_question_6,'')::int+COALESCE(t1.valeur_question_7)::int+COALESCE(t1.valeur_question_9,'')::int))
WHERE t1.APPLICATION = t1.APPLICATION;
But this query gaves me an error : invalid input syntax for integer: ""
I saw that the Postgres equivalent to MySQL is COALESCE() so i think i'm on the good way here.
I also know you cannot add varchar to varchar so i tried to cast it to integer to do that. I'm not sure if i casted it correctly with parenthesis at the good place and regarding to error maybe i cannot cast to int with coalesce.
Last thing, i can certainly do a co-related sub-select to update my two tables but i'm a little lost at this point.
The output must be an integer matching the number of questions answered to a backup survey.
Any thoughts?
Thanks.
coalesce() returns the first non-null value from the list supplied. So, if the column value is null the expression COALESCE(t1.valeur_question_4,'') returns an empty string and that's why you get the error.
But it seems you want something completely different: you want check if the column is null (or empty) and then subtract a value if it is to count the number of non-null columns.
To return 1 if a value is not null or 0 if it isn't you can use:
(nullif(valeur_question_4, '') is null)::int
nullif returns null if the first value equals the second. The IS NULL condition returns a boolean (something that MySQL doesn't have) and that can be cast to an integer (where false will be cast to 0 and true to 1)
So the whole expression should be:
nb_question_repondu = 9 - (
(nullif(t1.valeur_question_4,'') is null)::int
+ (nullif(t1.valeur_question_6,'') is null)::int
+ (nullif(t1.valeur_question_7,'') is null)::int
+ (nullif(t1.valeur_question_9,'') is null)::int
)
Another option is to unpivot the columns and do a select on them in a sub-select:
update reponse_question_finale
set nb_question_repondu = (select count(*)
from (
values
(valeur_question_4),
(valeur_question_6),
(valeur_question_7),
(valeur_question_9)
) as t(q)
where nullif(trim(q),'') is not null);
Adding more columns to be considered is quite easy then, as you just need to add a single line to the values() clause

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}'));