SQL query, which search all rows with specific items of array column - sql

my sql case in postgres:9.6 database
CREATE TABLE my_table (
id serial PRIMARY KEY,
numbers INT []
);
INSERT INTO my_table (numbers) VALUES ('{2, 3, 4}');
INSERT INTO my_table (numbers) VALUES ('{2, 1, 4}');
-- which means --
test=# select * from my_table;
id | numbers
----+---------
1 | {2,3,4}
2 | {2,1,4}
(2 rows)
I need to find all rows with numbers 1 and/or 2. According this answer I use query like this:
SELECT * FROM my_table WHERE numbers = ANY('{1,2}'::int[]);
And got following error:
LINE 1: SELECT * FROM my_table WHERE numbers = ANY('{1,2}'::int[]);
^
HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts.
How does correct sql query look?

Using var = ANY(array) works well for finding if a single value (var) is contained in an array.
To check if an array contains parts of another array, you would use the && operator
&& -- overlap (have elements in common) -- ARRAY[1,4,3] && ARRAY[2,1] --> true
SELECT * FROM my_table WHERE numbers && '{1,2}'::int[];
To check if an array contains all members of another array, you would use the #> operator
#> -- contains -- ARRAY[1,4,3] #> ARRAY[3,1] --> true
SELECT * FROM my_table WHERE numbers #> '{1,2}'::int[];

Related

Construct ARRAY of values from a subquery in Postgres and use it in a WHERE clause

These are samples of the two tables I have:
Table 1
material_id (int) codes (jsonb)
--------------------- -------------------------------
1 ['A-12','B-19','A-14','X-22']
2 ['X-106','A-12','X-22','B-19']
.
.
Table 2
user_id material_list (jsonb)
----------- --------------------
1 [2,3]
2 [1,2]
.
.
Table 1 contains material IDs and an array of codes associated with that material.
Table 2 contains user IDs. Each user has a list of materials associated with it and this is saved an an array of material IDs
I want to fetch a list of user IDs for all materials having certain codes. This is the query I tried, but it threw a syntax error:
SELECT user_id from table2
WHERE material_list ?| array(SELECT material_id
FROM table1 where codes ?| ['A-12','B-19]);
I am unable to figure out how to fix it.
Your query fails for multiple reasons.
First, ['A-12','B-19] isn't a valid Postgres text array. Either use an array constant or an array constructor:
'{A-12,B-19}'
ARRAY['A-12','B-19']
See:
How to pass custom type array to Postgres function
Pass array literal to PostgreSQL function
Next, the operator ?| demands text[] to the right, while you provide int[].
Finally, it wouldn't work anyway, as the operator ?| checks for JSON strings, not numbers. The manual:
Do any of the strings in the text array exist as top-level keys or array elements?
Convert the JSON array to a Postgres integer array, then use the array overlap operator &&
SELECT user_id
FROM tbl2
WHERE ARRAY(SELECT jsonb_array_elements_text(material_list)::int)
&& ARRAY(SELECT material_id FROM tbl1 where codes ?| array['A-12','B-19']);
I strongly suggest to alter your table to convert the JSON array in material_list to a Postgres integer array (int[]) for good. See:
Altering JSON column to INTEGER[] ARRAY
How to turn JSON array into Postgres array?
Then the query gets simpler:
SELECT user_id
FROM tbl2
WHERE material_list && ARRAY(SELECT material_id FROM tbl1 where codes ?| '{A-12,B-19}');
db<>fiddle here
Or - dare I say it? - properly normalize your relational design. See:
How to implement a many-to-many relationship in PostgreSQL?
This seems like the process of unnesting json arrays:
select t2.user_id
from table2 t2
where exists (select 1
from table1 t1 join
jsonb_array_elements_text(t2.material_list) j(material_id)
on t1.material_id = j.material_id::int join
jsonb_array_elements_text(t1.codes) j2(code)
on j2.code in ('A-12', 'B-19')
);
Here is a db<>fiddle.

Compare two arrays in PostgreSQL

I have a table in postgres with a value column that contains string arrays. My objective is to find all arrays that contain any of the following strings: {'cat', 'dog'}
id value
1 {'dog', 'cat', 'fish'}
2 {'elephant', 'mouse'}
3 {'lizard', 'dog', 'parrot'}
4 {'bear', 'bird', 'cat'}
The following query uses ANY() to check if 'dog' is equal to any of the items in each array and will correctly return rows 1 and 3:
select * from mytable where 'dog'=ANY(value);
I am trying to find a way to search value for any match in an array of strings. For example :
select * from mytable where ANY({'dog', 'cat'})=ANY(value);
Should return rows 1, 3, and 4. However, the above code throws an error. Is there a way to use the ANY() clause on the left side of this equation? If not, what would be the workaround to check if any of the strings in an array are in value?
You can use && operator to find out whether two array has been overlapped or not. It will return true only if at least one element from each array match.
Schema and insert statements:
create table mytable (id int, value text[]);
insert into mytable values (1,'{"dog", "cat", "fish"}');
insert into mytable values (2,'{"elephant", "mouse"}');
insert into mytable values (3,'{"lizard", "dog", "parrot"}');
insert into mytable values (4,'{"bear", "bird", "cat"}');
Query:
select * from mytable where array['dog', 'cat'] && (value);
Output:
id
value
1
{dog,cat,fish}
3
{lizard,dog,parrot}
4
{bear,bird,cat}
db<>fiddle here

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

Oracle search array within JSON values?

I have the following stored in an Oracle database as JSON:
{
value: [1,2,3]
}
The value can be of any type (strings, integers or arrays). How would I query if the type is array and if it contains a certain value?
In pseudocode:
SELECT * FROM TABLE WHERE COLUMN_NAME.value CONTAINS 2
I can see how to query strings using Oracle functions such as json_query but cannot see how to run this specific type of query without selecting all data and searching on the client.
You may use JSON_TABLE in the FROM, defining the columns and then use it in where clause to filter rows.
--Test data
with t (id,j)
as
( select 1, TO_CLOB(
'{
value : [1,2,3]
}') FROM DUAL
)
--Test data ends--
select t.id,tbl.val FROM t cross join
json_table(j,'$.value[*]' columns (val varchar2(100) path '$') ) as tbl
where tbl.val = 2
ID VAL
------ -------
1 2

JSONB array contains like OR and AND operators

Consider a table temp (jsondata jsonb)
Postgres provides a way to query jsonb array object for contains check using
SELECT jsondata
FROM temp
WHERE (jsondata->'properties'->'home') ? 'football'
But, we can't use LIKE operator for array contains. One way to get LIKE in the array contains is using -
SELECT jsondata
FROM temp,jsonb_array_elements_text(temp.jsondata->'properties'->'home')
WHERE value like '%foot%'
OR operation with LIKE can be achieved by using -
SELECT DISTINCT jsondata
FROM temp,jsonb_array_elements_text(temp.jsondata->'properties'->'home')
WHERE value like '%foot%' OR value like 'stad%'
But, I am unable to perform AND operation with LIKE operator in JSONB array contains.
After unnesting the array with jsonb_array_elements() you can check values meeting one of the conditions and sum them in groups by original rows, example:
drop table if exists temp;
create table temp(id serial primary key, jsondata jsonb);
insert into temp (jsondata) values
('{"properties":{"home":["football","stadium","16"]}}'),
('{"properties":{"home":["football","player","16"]}}'),
('{"properties":{"home":["soccer","stadium","16"]}}');
select jsondata
from temp
cross join jsonb_array_elements_text(temp.jsondata->'properties'->'home')
group by jsondata
-- or better:
-- group by id
having sum((value like '%foot%' or value like 'stad%')::int) = 2
jsondata
---------------------------------------------------------
{"properties": {"home": ["football", "stadium", "16"]}}
(1 row)
Update. The above query may be expensive with a large dataset. There is a simplified but faster solution. You can cast the array to text and apply like to it, e.g.:
select jsondata
from temp
where jsondata->'properties'->>'home' like all('{%foot%, %stad%}');
jsondata
---------------------------------------------------------
{"properties": {"home": ["football", "stadium", "16"]}}
(1 row)
I have the following, but it was a bit fiddly. There's probably a better way but this is working I think.
The idea is to find the matching JSON array entries, then collect the results. In the join condition we check the "matches" array has the expected number of entries.
CREATE TABLE temp (jsondata jsonb);
INSERT INTO temp VALUES ('{"properties":{"home":["football","stadium",16]}}');
SELECT jsondata FROM temp t
INNER JOIN LATERAL (
SELECT array_agg(value) AS matches
FROM jsonb_array_elements_text(t.jsondata->'properties'->'home')
WHERE value LIKE '%foo%' OR value LIKE '%sta%'
LIMIT 1
) l ON array_length(matches, 1) = 2;
jsondata
-------------------------------------------------------
{"properties": {"home": ["football", "stadium", 16]}}
(1 row)
demo: db<>fiddle
I would cast the array into text. Then you are able to search for keywords with every string operator.
Disadvantage: because it was an array the text contains characters like braces and commas. So it's not that simple to search for keyword with a certain beginning (ABC%): You always have to search like %ABC%
SELECT jsondata
FROM (
SELECT
jsondata,
jsondata->'properties'->>'home' as a
FROM
temp
)s
WHERE
a LIKE '%stad%' AND a LIKE '%foot%'