Extract elements from an array inside a jsonb field in Postgresql - sql

In my Postgresql table, I have a jsonb field called data, which contains data in the following format:
{
list:[1,2,3,4,5]
}
I use the query:
select data->'list' from "Table" where id=1
This gives me the array [1,2,3,4,5]
The problem is that I want to use this result in another select query within the IN clause. It's not accepting the array.
IN ([1,2,3,4,5]) fails
It wants:
IN (1,2,3,4,5)
So, In my original query I don't know how to covert [1,2,3,4,5] to just 1,2,3,4,5
My current query is:
select * from "Table2" where "items" in (select data->'list' from "Table" where id=1)
Please help

You can use the array contains operator (#>) rather than IN if you cast the search value to jsonb. For example:
SELECT *
FROM "Table2"
WHERE items::jsonb <# (SELECT data->'list' FROM "Table" WHERE id=1)
Note that if items is an int you will need to cast it char before casting to jsonb:
SELECT *
FROM "Table2"
WHERE cast(items as char)::jsonb <# (SELECT data->'list' FROM "Table" WHERE id=1)
Demo on dbfiddle

Use jsonb_array_elements() to turn the elements into rows
select t2.*
from table_2 t2
where t2.items in (select jsonb_array_elements_text(t1.data -> 'list')::int
from table_1 t1
where t1.id = 1);
This assumes that items is defined as text or varchar and contains a single value - however the name (plural!) seems to indicate yet another de-normalized column.

Related

PostgreSQL JSONB overlaps operator on multiple JSONB columns

I have a table that contains two jsonb columns both consisting of jsonb data that represent an array of strings. These can be empty arrays too.
I am now trying to query this table and retrieve the rows where either (or both) jsonb arrays contain at least one item of an array I pass, I managed to figure out a working query
SELECT *
FROM TABLE T
WHERE (EXISTS (SELECT *
FROM JSONB_ARRAY_ELEMENTS_TEXT(T.DATA1) AS DATA1
WHERE ARRAY[DATA1] && ARRAY['some string','some other string']))
OR (EXISTS (SELECT *
FROM JSONB_ARRAY_ELEMENTS_TEXT(T.DATA2) AS DATA2
WHERE ARRAY[DATA2] && ARRAY['random string', 'another random string']));
But i think this is not optimal at all, I am trying to do it with a cross join but the issue is that this data1 and data2 in the jsonb columns can be an empty array, and then the join will exclude these rows, while maybe the other jsonb column does satisfy the overlaps && condition.
I tried other approaches too, like:
SELECT DISTINCT ID
FROM table,
JSONB_ARRAY_ELEMENTS_TEXT(data1) data1,
JSONB_ARRAY_ELEMENTS_TEXT(data2) data2
WHERE data1 in ('some string', 'some other string')
OR data2 in ('random string', 'string');
But this one also does not include rows where data1 or data2 is an empty string. So I thought of a FULL OUTER JOIN but because this is a lateral reference it does not work:
The combining JOIN type must be INNER or LEFT for a LATERAL reference.
You don't need to unnest the JSON array. The JSONB operator ?| can do that directly - it checks if any of the array elements of the argument on the right hand side is contained as a top-level element in the JSON value on the left hand side.
SELECT *
FROM the_table t
WHERE t.data1 ?| ARRAY['some string','some other string']))
OR t.data2 ?| ARRAY['random string', 'another random string']));
This will not return rows where both array are empty (or if neither of the columns contains the searched keys)

Remove long key value pairs in jsonb column in postgres with SQL

I am using a materialized view to merge an query 3 json columns because I want to query all of them together with 1 GIN index. The view looks similar to this:
CREATE MATERIALIZED VIEW IF NOT EXISTS test_materialized_view AS
SELECT t1.id, (t1.data1 || t1.data2 || COALESCE(t2.data1, '{}'::jsonb)) "data"
FROM table_1 t1 LEFT JOIN table_2 t2 ON (...);
Now it can happen that there are longer key value pairs in the json data which I never want to query and which can be stored 1000 of times because they are in t2.data1 . Is it possible to filter the merged json and only include key value pairs with a length less than x characters? Does this even make a difference / reduce saved data?
I dont know the json keys of these fields. I basically just want to remove all key value pairs which are longer than x characters or array / nested objects but did not really find a good way to do this in postgres
There is no built-in function for this. You will need to write your own.
Something along the lines:
create function remove_long_values(p_input jsonb, p_maxlen int)
returns jsonb
as
$$
select coalesce(jsonb_object_agg(e.ky, e.val), '{}')
from jsonb_each(p_input) as e(ky,val)
where length(e.val::text) <= p_maxlen;
$$
language sql
immutable
parallel safe;
The above does not deal with nested key/value pairs! It only checks this on the first level.
Then use it in the query:
CREATE MATERIALIZED VIEW IF NOT EXISTS test_materialized_view
AS
SELECT t1.id, t1.data1 || t1.data2 || remove_long_values(t2.data1,250) as "data"
FROM table_1 t1
LEFT JOIN table_2 t2 ON (...);

SELECT on JSON operations of Postgres array column?

I have a column of type jsonb[] (a Postgres array of jsonb objects) and I'd like to perform a SELECT on rows where a criteria is met on at least one of the objects. Something like:
-- Schema would be something like
mytable (
id UUID PRIMARY KEY,
col2 jsonb[] NOT NULL
);
-- Query I'd like to run
SELECT
id,
x->>'field1' AS field1
FROM
mytable
WHERE
x->>'field2' = 'user' -- for any x in the array stored in col2
I've looked around at ANY and UNNEST but it's not totally clear how to achieve this, since you can't run unnest in a WHERE clause. I also don't know how I'd specify that I want the field1 from the matching object.
Do I need a WITH table with the values expanded to join against? And how would I achieve that and keep the id from the other column?
Thanks!
You need to unnest the array and then you can access each json value
SELECT t.id,
c.x ->> 'field1' AS field1
FROM mytable t
cross join unnest(col2) as c(x)
WHERE c.x ->> 'field2' = 'user'
This will return one row for each json value in the array.

Getting selection of structs from an array of structs in BQ

I have a table where one column is defined as:
my_column ARRAY<STRUCT<key STRING, value FLOAT64, description STRING>>
Is there some easy way how to specify list of parameters to be returned in a SELECT statement? For instance removing description, so the result column would be still an array of structs but containing only key and value.
Below is for BigQuery Standard SQL
#standardSQL
SELECT * REPLACE(
ARRAY(
SELECT AS STRUCT * EXCEPT(description)
FROM UNNEST(my_column)
) AS my_column)
FROM `project.dataset.table`
Above fully preserves schema of table and only does change in my_column field by removing description
I would just unnest and then re-aggregate your selected fields.
select array_agg(struct(m.key,m.value)) as my_new_column
from table
left join unnest(my_column) m
I found this way:
SELECT
ARRAY(SELECT AS VALUE STRUCT(key, value) FROM a.my_column) as my_new_column
FROM my_table a
No joining or unnesting needed.

Postgres statement for JSON_VALUE

what is the postgres statement for this SQL statement.
SELECT * FROM table1 where JSON_VALUE(colB,'$.Items[0].Item') ='abc'
i have tried follow postgres document but result
No function matches the given name and argument types
You can use the -> operator to access an element in an index.
SELECT *
FROM table1
where colb -> 'Items' -> 0 ->> 'Item' = 'abc'
colb -> 'Items' -> 0 returns the first array element of Items as a JSON value. And ->> 'Item' then returns the key "Item" from within that JSON as a text (aka varchar) value.
This requires that colb is defined as jsonb (or at least json). If not, you need to cast it like this colb::jsonb.
But in the long run you should really convert that column to jsonb then.
If you want to search for Item = 'abc' anywhere in the Items array (not just position 0), you can use the #> operator:
select *
from data
where colb #> '{"Items": [{"Item": "abc"}]}';
Online example: https://rextester.com/BQWB24156
The above can use a GIN index on the column colb. The first query will require an index on that expression.
With Postgres 12 you can use a JSON path query like you have:
SELECT *
FROM table1
where jsonb_path_exists(colb, '$.Items[0].Item' ? (# == "abc")');
If you want to search anywhere in the array, you can use:
SELECT *
FROM table1
where jsonb_path_exists(colb, '$.Items[*].Item' ? (# == "abc")');
That again can not make use of a GIN index on the column, it would require an index on that expression
Something like this.
SELECT t.*
FROM table1 t
cross join json_array_elements(colb->'Items') as j
where j->>'Item' = 'abc'
DEMO