Postgresql add item in array except if null - sql

I try to make a PostgreSQL array by using values from JSONB field.
When all data are in JSONB, result is fine. This query :
SELECT array [
(SELECT ('{"tech_id": 4, "admin_id": 5}'::jsonb->>'admin_id')::int),
(SELECT ('{"tech_id": 4, "admin_id": 5}'::jsonb->>'tech_id')::int)
];
returns me the right result because admin_id and tech_id are in JSONB field :
-[ RECORD 1 ]
array | {5,4}
But, if JSONB contains only one value, the array contains a NULL value.
This query :
SELECT array [
(SELECT ('{"tech_id": 4}'::jsonb->>'admin_id')::int),
(SELECT ('{"tech_id": 4}'::jsonb->>'tech_id')::int)
];
Gives me this result :
-[ RECORD 1 ]---
array | {NULL,4}
But I want an array like {4}, so without NULL value.
Do you know a way to avoid adding NULL value in this case ?

Finnaly, I found solution by wrapping the array to check if values are NULL.
I use the following code :
select array_agg(id) FROM unnest(array [
(SELECT ('{"tech_id": 4}'::jsonb->>'admin_id')::int),
(SELECT ('{"tech_id": 4}'::jsonb->>'tech_id')::int)
]) as id where id IS NOT NULL;
PostgreSQL returns the expected result :
-[ RECORD 1 ]--
array_agg | {4}

Related

PostgresSQL: Select rows where a JSONB field contains all items in an array

Given the database table below where tags are arrays stored as JSONB, how can I write an SQL query to only select ids that contain all tags value's in an array?
e.g only records 1 and 2 would be returned for the array {"London", "Sydney"}
id | tags
----+---------------------------------------------------
1 | [{"value": "Sydney"..}, {"value": "London"..}, {"value": "Munich"..}]
2 | [{"value": "Sydney"..}, {"value": "London"..}]
3 | [{"value": "London"..}]
4 | [{"value": "Sydney"..}]
I managed to construct this query however it does not return an absolute match for ALL the items in the array which is what I'm after..
SELECT *
FROM mytable
WHERE EXISTS (
SELECT TRUE
FROM jsonb_array_elements(tags) tag
WHERE tag->>'value' IN ('London', 'Sydney')
)
You can use the contains operator #> with a JSON array:
select *
from the_table
where tags #> '[{"value": "London"}, {"value": "Sydney"}]'
Or you can use a JSON path expression
select *
from the_table
where jsonb_path_query_array(tags, '$[*].value') ?& array['London', 'Sydney']

Using json_build_object in PostgreSQL v14.x with the result of a SELECT statement

I'm trying to create a VIEW of a JSON object, with a varying number of key/value pairs, in PostgreSQL v14.x, from the results of a SELECT statement.
Using json_agg is returning an array of objects - a key of each rating possibility as they occur, and a value which is the count of all the ratings selected from a table of reviews. Instead of an array, I need an object that has multiple key/value pairs, where the value corresponds to the aggregated count() of the ratings column(s), grouped by product_id. Trying to reuse json_build_object isn't working as expected.
Using:
CREATE VIEW reviews.meta AS
SELECT product_id, json_agg(json_build_object(reviews.list.rating, reviews.list.rating))
FROM reviews.list
GROUP BY reviews.list.product_id
ORDER BY product_id;
returns:
product_id | reviews_count
---------------------------
1 | [{"5" : 5}, {"4" : 4}]
2 | [{"4" : 4}, {"4" : 4}, {"3" : 3}, {"5" : 5}, {"2" : 2}]
But I'm looking for:
product_id | reviews_count
---------------------------
1 | {"5" : 1, "4" : 1}
2 | {"4" : 2, "3" : 1, "5" : 1, "2" : 1}
A dynamically created object:
in rows by product_id
where the values are quantities of Integer ratings (1-5) as they appear in the reviews.list table
in an object rather than an array of objects
I am new to SQL / PL/pgSQL language.
You need two levels of aggregation, one to get the counts, and one to package the counts up. It is easy to do that by nesting one query inside the FROM of another:
CREATE or replace VIEW meta AS
SELECT product_id, jsonb_object_agg(rating, count)
FROM (select product_id, rating, count(*) from list group by product_id, rating) foo
GROUP BY product_id
ORDER BY product_id;

How to extract value from json in Postgress based on a key pattern?

In Postgres v9.6.15, I need to get the SUM of all the values using text patterns of certain keys.
For example, having table "TABLE1" with 4 rows:
|group |col1
======================================
Row #1:|group1 |{ "json_key1.id1" : 1 }
Row #2:|group1 |{ "json_key1.id2" : 1 }
Row #3:|group1 |{ "json_key2.idX" : 1 }
Row #4:|group1 |{ "not_me" : 2 }
I'd like to get the int values using a pattern of the first part of the keys ( "json_key1" and "json_key2" ) and SUM them all using a CASE block like so:
SELECT table1.group as group,
COALESCE(sum(
CASE
WHEN table1.col1 = 'col1_val1' THEN (table1.json_col->>'json_key1.%')::bigint
WHEN table1.col1 = 'col1_val2' THEN (table1.json_col->>'json_key2.%')::bigint
ELSE 0::bigint
END), 0)::bigint AS my_result
FROM table1 as table1
GROUP BY table1.group;
I need "my_result" to look like:
|group |my_result
======================================
Row #1:|group1 |3
Is there a way to collect the values using regex or something like that. Not sure if I am checking the right documentation ( https://www.postgresql.org/docs/9.6/functions-json.html ), but I am not finding anything that can help me to achieve the above OR if that is actually possible..
Use jsonb_each_text() in a lateral join to get pairs (key, value) from json objects:
select
group_col as "group",
coalesce(sum(case when key like 'json_k%' then value::numeric end), 0) as my_result
from table1
cross join jsonb_each_text(col1)
group by group_col
Db<>fiddle.

How to create SQL query to sort JSON array using 1 attribute?

I have a table with a column that contains a JSON body that has arrays that I want to sort based on a attribute associated with that array.
I have tried selecting the array name and displaying the attribute which will display the entire array
The column name is my_column and the JSON is formatted as follows -
{
"num": "123",
"Y/N": "Y",
"array1":[
{
"name": "Bob",
"sortNum": 123
},
{
"name": "Tim Horton",
"sortNum": 456
}
]
}
I want the output to be based on the highest value of sortNum so the query should display the attributes for Tim Horton. The code I have played around with is below but get an error when trying to query based on sortNum.
SELECT my_column
FROM
my_table,
jsonb_array_elements(my_column->'array1') elem
WHERE elem->>'sortNum' = INT
Order by the filed 'sortNum' of the array element descending and use LIMIT 1 to only get the top record.
SELECT jae.e
FROM my_table t
CROSS JOIN LATERAL jsonb_array_elements(t.my_column->'array1') jae (e)
ORDER BY jae.e->'sortNum' DESC
LIMIT 1;
Edit:
If you want to sort numerically rather than lexicographically, get the element as text and cast it to an integer prior sorting on it.
SELECT jae.e
FROM my_table t
CROSS JOIN LATERAL jsonb_array_elements(t.my_column->'array1') jae (e)
ORDER BY (jae.e->>'sortNum')::integer DESC
LIMIT 1;
This answer assumes that your table has a column (or maybe a combination of columns) that can be use to uniquely identify a record. Let's call it myid.
To start with, we can use json_array_elements to split the JSON array into rows, as follows:
select myid, x.value, x->>'sortNum'
from
mytable,
json_array_elements(mycolumn->'array1') x
;
This returns:
myid | value | sortnum
---------------------------------------------------------
1 | {"name":"Bob","sortNum":123} | 123
1 | {"name":"Tim Horton","sortNum":456} | 456
Now, we can turn this to a subquery, and use ROW_NUMBER() to filter in the array element with the highest sortNum attribute:
select value
from (
select
x.value,
row_number() over(partition by myid order by x->>'sortNum' desc) rn
from
mytable,
json_array_elements(mycolumn->'array1') x
) y
where rn = 1;
Yields:
value
-----------------------------------
{"name":"Tim Horton","sortNum":456}
Demo on DB Fiddle

Query with filter on value in a jsonb array

I have a column with type jsonb holding a list of IDs as plain JSON array in my PostgreSQL 9.6.6 database and I want to search this field based on any ID in the list. How to query write this query?
'[1,8,3,4,56,6]'
For example, my table is:
CREATE TABLE mytable (
id bigint NOT NULL,
numbers jsonb
);
And it has some values:
id | numbers
-----+-------
1 | "[1,8,3,4,56,6]"
2 | "[1,2,7,4,24,5]"
I want something like this:
SELECT *
FROM mytable
WHERE
id = 1
AND
numbers::json->>VALUE(56)
;
Expected result (only if the JSON array has 56 as element):
id | numbers
-----+-------
1 | "[1,8,3,4,56,6]"
Step-2 problem :
The result of this command is TRUE :
SELECT '[1,8,3,4,56,6]'::jsonb #> '56';
but already when I use
SELECT *
FROM mytable
numbers::jsonb #> '[56]';
or
SELECT *
FROM mytable
numbers::jsonb #> '56';
or
SELECT *
FROM mytable
numbers::jsonb #> '[56]'::jsonb;
The result is nothing :
id | numbers
-----+-------
(0 rows)
Instead of be this :
id | numbers
-----+-------
1 | "[1,8,3,4,56,6]"
I find why I get (0 rows) ! :))
because I insert jsonb value to mytable with double quotation , in fact this is correct value format (without double quotation ):
id | numbers
-----+-------
1 | [1,8,3,4,56,6]
2 | [1,2,7,4,24,5]
now when run this command:
SELECT *
FROM mytable
numbers #> '56';
The result is :
id | numbers
-----+-------
1 | [1,8,3,4,56,6]
Use the jsonb "contains" operator #>:
SELECT *
FROM mytable
WHERE id = 1
AND numbers #> '[56]';
Or
...
AND numbers #> '56';
Works with our without enclosing array brackets in this case.
dbfiddle here
This can be supported with various kinds of indexes for great read performance if your table is big.
Detailed explanation / instructions:
Index for finding an element in a JSON array
Hint (addressing your comment): when testing with string literals, be sure to add an explicit cast:
SELECT '[1,8,3,4,56,6]'::jsonb #> '56';
If you don't, Postgres does not know which data types to assume. There are multiple options:
SELECT '[1,8,3,4,56,6]' #> '56';
ERROR: operator is not unique: unknown #> unknown
Related:
GIN index on smallint[] column not used or error "operator is not unique"