postgres && - Array Overlap operator with wildcard - sql

In postgres
select array['some', 'word'] && array ['some','xxx'] -- return true
select array['some', 'word'] && array ['','word'] -- return true
I'd like to know how I can use % wildcar in combination with && operator.
select array['some%', 'word'] && array ['','some'] -- I was thinking this will return true but it doesn't.
I want to check if a text array contains at least one element of another text array. The first text array can contains wildcard. What's the best way to do that ?

You could try unnest to parse every element of both arrays and compare them using LIKE or ILIKE:
SELECT EXISTS(
SELECT
FROM unnest(array['some%', 'word']) i (txt),
unnest(array ['','some']) j (txt)
WHERE j.txt LIKE i.txt) AS overlaps;
overlaps
----------
t
(1 row)
If you want to apply the % to all array elements, just place it directly in the WHERE clause in the LIKE or ILIKE operator:
SELECT EXISTS(
SELECT
FROM unnest(array['some', 'word']) i (txt),
unnest(array ['','XXsomeXX']) j (txt)
WHERE j.txt LIKE '%'||i.txt||'%') AS overlaps;
overlaps
----------
t
(1 row)
Demo: db<>fiddle

Related

Check if any of array elements starts with specific characters postgres

I am trying to check if an array has any of elements which starts with or ends with the string specified.
For Comparing strings we could do something like below,
select * from table where 'gggggg' ilike '%g';
Can someone help to find out if an array contains values as like pattern.
Eg array : ['str1', 'str2', 'str3']
Also I want to find if any of elements ends with 1 or starts with 'str'.
For now, the only thing you can do is unnest the array and test each element:
create table test (a text[]);
insert into test values (array['abc', 'def', 'ghi']);
select distinct a from test
JOIN lateral (select * from unnest(a) as u) as sub on TRUE
WHERE u like '%g';
a
---
(0 rows)
select distinct a from test
JOIN lateral (select * from unnest(a) as u) as sub on TRUE
WHERE u like 'g%';
a
---------------
{abc,def,ghi}
(1 row)
In postgres 12, you will be able to use jsonb_path_exists. Of course, this would work better if you stored your data in jsonb, but it will still work, just not as efficiently:
-- Starts with g
select a from test
where jsonb_path_exists(to_jsonb(a), '$[*] ? (# like_regex "^g")');
a
---------------
{abc,def,ghi}
(1 row)
-- Ends with g
select a from test
where jsonb_path_exists(to_jsonb(a), '$[*] ? (# like_regex "g$")');
a
---
(0 rows)

Extract a string in Postgresql and remove null/empty elements

I Need to extract values from string with Postgresql
But for my special scenario - if an element value is null i want to remove it and bring the next element 1 index closer.
e.g.
assume my string is: "a$$b"
If i will use
select string_to_array('a$$b','$')
The result is:
{a,,b}
If Im trying
SELECT unnest(string_to_array('a__b___d_','_')) EXCEPT SELECT ''
It changes the order
1.d
2.a
3.b
order changes which is bad for me.
I have found a other solution with:
select array_remove( string_to_array(a||','||b||','||c,',') , '')
from (
select
split_part('a__b','_',1) a,
split_part('a__b','_',2) b,
split_part('a__b','_',3) c
) inn
Returns
{a,b}
And then from the Array - i need to extract values by index
e.g. Extract(ARRAY,2)
But this one seems to me like an overkill - is there a better or something simpler to use ?
You can use with ordinality to preserve the index information during unnesting:
select a.c
from unnest(string_to_array('a__b___d_','_')) with ordinality as a(c,idx)
where nullif(trim(c), '') is not null
order by idx;
If you want that back as an array:
select array_agg(a.c order by a.idx)
from unnest(string_to_array('a__b___d_','_')) with ordinality as a(c,idx)
where nullif(trim(c), '') is not null;

Return boolean if one element matches among 2 arrays in postgresql

I have 2 arrays in postgresql and I need to return true if there are at least 1 match of element between these 2 arrays no mather if it is same position.
Follows example below:
select array(select generate_series(0,10)) =
any(select array(select generate_series(10,11)))
it should return true because I have 10 in both arrays
https://www.postgresql.org/docs/current/static/functions-array.html
= equal
&& overlap (have elements in common)
formatting mine. You need other operator
select array(select generate_series(0,10)) &&
any(select array(select generate_series(10,11)));
?column?
----------
t
(1 row)
Go for && operator
like
SELECT ARRAY['apple','cherry','avocado'] && ARRAY['applea','cherrya','avocado']
SELECT ARRAY[1,4,3] && ARRAY[2,1]
https://www.postgresql.org/docs/9.6/static/functions-array.html

Check if any substring from array appear in string?

I have the following query:
select case when count(*)>0 then true else false end
from tab
where param in ('a','b') and position('T' in listofitem)>0
This checks if 'T' exists in the column listofitem and if it does the count is > 0. Basically it's a search for sub string.
This works well in this private case. However my real case is that I have text[] called sub_array meaning multiple values to check. How can I modify the query to handle the sub_array type? I prefer to have it in a query rather than a function with a LOOP.
What I actualy need is:
select case when count(*)>0 then true else false end
from tab
where param in ('a','b') and position(sub_array in listofitem)>0
This is not working since sub_array is of type Text[]
Use the unnest() function to expand your array & bool_and() (or bool_or() -- this depends on what you want match: all array elements, or at least one) to aggregate:
select count(*) > 0
from tab
where param in ('a','b')
and (select bool_and(position(u in listofitem) > 0)
from unnest(sub_array) u)
A brute force method would be to convert the array to a string:
select (count(*) > 0) as flag
from tab
where param in ('a','b') and
array_to_string(listofitem, '') like '%T%';
I should note that comparing count(*) is not the most efficient way of doing this. I would suggest instead:
select exists (select 1
from tab
where param in ('a','b') and
array_to_string(listofitem, '') like '%T%'
) as flag;
This stops the logic at the first match, rather than counting all matching rows.

PostgreSQL two dimensional array intersection

is it possible with postgres tools to intersect two-dimensional arrays?
For instance I have:
id arr
1 {{1,2}, {3,4}, {4,5}, {4,7}}
2 {{4,2}, {7,4}, {8,5}, {9,7}}
...
And I want to get all records that have {4,5} in theirs array, record id=1 here.
If I do something like:
select * from table where arr && '{4,5}'
I'll get both of those example records. It finds all records where are 4 and 5 are anywhere in the array - as if it was looking in fully extracted arrays, like {1,2,3,4,4,5,4,7}.
I thought of using functions from the extension intarray, but:
Many of these operations are only sensible for one-dimensional arrays.
Although they will accept input arrays of more dimensions, the data is
treated as though it were a linear array in storage order.
My other ideas:
Quick and dirty
WITH x(id, arr) AS (VALUES
(1, '{{1,2}, {3,4}, {4,5}, {4,7}}'::int[])
,(2, '{{4,2}, {7,4}, {8,5}, {9,7}}')
)
SELECT *
FROM x
WHERE arr::text LIKE '%{4,5}%';
The simple trick is to transform the array to its text representation and check with LIKE.
With array-functions
WITH x(id, arr) AS (
VALUES
(1, '{{1,2}, {3,4}, {4,5}, {4,7}}'::int[])
,(2, '{{4,2}, {7,4}, {8,5}, {9,7}}')
)
,y AS (
SELECT id, arr, generate_subscripts(arr, 1) AS i
FROM x
)
SELECT id, arr
FROM y
WHERE arr[i:i] = '{{4,5}}'::int[];
Or, the same in different notation, building on your example:
SELECT id, arr
FROM (
SELECT id, arr, generate_subscripts(arr, 1) AS i
FROM tbl
) x
WHERE arr[i:i] = '{{4,5}}'::int[];
This generates the array subscripts for the first dimension. With the help of this set-returning function we check each array-slice. Note the double brackets on the right hand side of the expression to create a two-dimensional array that matches.
If {4,5} can be in arr multiple times, add GROUP BY or a DISTINCT clause to avoid duplicate rows.