Use ilike any() with escape character - postgresql

In PostgreSQL you can do a case insensitive query with ILIKE:
select * from test where value ilike 'half is 50$%' escape '$'
And you can query multiple values at once by combining ILIKE with ANY()
select * from test where value ilike any(array['half is 50%', 'fifth is 20%'])
The query above will match 'Fifth is 2019', which I do not want, but when I try to use ILIKE and ANY() with an escape character I get a syntax error.
Am I missing something stupid, or is this simply not supported? If not, is there another way to query in a case insensitive way with multiple values at once?
EDIT: To clarify, the query will accept parameters through JDBC, so the actual SQL will look something like
select * from test where value ilike any(?) escape '$'
This is why I'm looking make % and _ from the user input be interpreted as literals.

The ESCAPE clause in ILIKE refers only to literals and does not apply to expressions. You should use a backslash, or if not possible, you can try:
with test(value) as (
values
('half is 50%'),
('half is 50x'),
('fifth is 20%'),
('fifth is 2000')
)
select *
from test
where value ilike any(select replace(unnest(array['half is 50$%', 'fifth is 20$%']), '$', '\'))
value
--------------
half is 50%
fifth is 20%
(2 rows)
Looks a bit clumsy but works well.

To match them as raw strings, you may use the ~* operator for insensitive match.
knayak=# select 'Half is 50%' ~* any(array['half is 50%', 'fifth is 20%'])
knayak-# ;
?column?
----------
t --True
(1 row)
knayak=# select 'fifth is 20' ~* any(array['half is 50%', 'fifth is 20%']);
?column?
----------
f --False
(1 row)
If you wish to escape the right hand operands of ilike use "escape" string constants, which are an extension to the SQL standard. An escape string constant is specified by writing the letter E (upper or lower case) just before the opening single quote
knayak=# select 'Half is 50%' ilike any(array[E'half is 50\\%', E'half is 20\\%'])
knayak-# ;
?column?
----------
t
(1 row)
DEMO

Related

postgressql query matching a field containing domainname

Need help with postsql query field matching a certain domain names in the end as per below in a particular FIELD.
1234.abc.xyz.com;
0971.abc.xyz.com
WHERE CAST (domain_name AS text) LIKE '%\d{4}.abc.xyz.com%'
#where domain_name is the FIELD name
~ is used for regular expression matching, LIKE for simple matching. Read more about them here: https://www.postgresql.org/docs/current/functions-matching.html#FUNCTIONS-SIMILARTO-REGEXP
If you just want to find domain_name that end in a particular text, the simple matching works fine (don't know if you really need the cast):
select * from tbl_test where domain_name LIKE '%.abc.xyz.com'
This will not work correctly:
select * from tbl_test where domain_name ~ '\d\d\d\d.abc.xyz.com'
The dot (.) is "any character" in a regular expression so this domain would be selected: abcd.abcxxyzdcom. You need to escape the dot in the string for it to be treated literally like this: '\d\d\d\d\.abc\.xyz\.com'
Underscore is a wildcard for "any character" in the simple LIKE.
use ~ followed by your search pattern as regular expression:
where domain_name ~ '\d\d\d\d\.abc\.xyz\.com'
playground:
https://dbfiddle.uk/O0Q_Ctmo
create table tbl_test (
domain_name varchar
);
insert into tbl_test VALUES
('1234.abc.xyz.com'),
('0971.abc.xyz.com'),
('0971.abc.xyz.bam'),
('1234.xxx.xyz.com'),
('123.xxx.xyz.com'),
('aaaa.xxx.xyz.com')
CREATE TABLE
INSERT 0 6
select * from tbl_test
where domain_name ~ '\d\d\d\d\.abc\.xyz\.com'
domain_name
1234.abc.xyz.com
0971.abc.xyz.com
SELECT 2
fiddle

Convert String to Array - PostgreSQL

I have a column in a table that stores names separated by commas, example: "Mel's Hou Rest, Mel's Lad Rest". What I need is to convert this string into an array separated by commas.
The query I need is:
SELECT home_location, subs_state FROM cust
WHERE (home_location = ANY('{"Mel''s Hou Rest", Mel''s Lad Rest"}')) AND subs_state = 'active'
I have tried this, but I keep getting an error:
WHERE (home_location = ANY(string_to_array("Mel's Hou Rest, Mel's Lad Rest", ',')::text[])
Is there any way to accomplish this without me having to change the database from 'text' to 'array'
SQL uses single quotes for string literals. Your string "Mel's Hou Rest, Mel's Lad Rest" has double quotes around it which makes Postgres interpret it as an quoted identifier. You can use two single quotes to include one in the string.
SELECT * FROM cust WHERE home_location = ANY(string_to_array("Mel's Hou Rest, Mel's Lad Rest", ','))
-- ERROR: column "Mel's Hou Rest, Mel's Lad Rest" does not exist
SELECT * FROM cust WHERE home_location = ANY(string_to_array('Mel''s Hou Rest, Mel''s Lad Rest', ','))
-- OK
Also note that string_to_array does not remove whitespace around the delimiter which might not be what you expect.
For example:
-- With whitespace around the delimiter
=> SELECT string_to_array('foo, bar', ',')
string_to_array
-----------------
{foo," bar"}
=> select 'foo' = ANY(string_to_array('foo, bar', ','));
?column?
----------
t
=> select 'bar' = ANY(string_to_array('foo, bar', ','));
?column?
----------
f
-- Without extra whitespace
=> SELECT string_to_array('foo,bar', ',')
string_to_array
-----------------
{foo,bar}
=> select 'foo' = ANY(string_to_array('foo,bar', ','));
?column?
----------
t
=> select 'bar' = ANY(string_to_array('foo,bar', ','));
?column?
----------
t
This of course can be countered by normalising the input before using it in the query. In somes cases it might be feasible to strip the whitespace in the query with string_to_array(regexp_replace('foo, bar', '\s*,\s', ','), ',') but I would not complicate the queries like that without a good reason.
To supplement the accepted answer a bit...
Note that the array string literal notation (with curly braces) '{foo, ba''r, "b{a}z", "b,u,z"}' (equivalent to the more explicit ARRAY['foo', 'ba''r', 'b{a}z', 'b,u,z']) is still actually just a string. To be used as an array it needs to first be converted, which a few operations can do implicitly (like ANY()). In many cases though, you'd need to explicitly cast it (e.g. with CAST(array_literal as text[]) or array_literal::text[]).
Your first expression should therefore work if rewritten as
SELECT home_location, subs_state FROM cust
WHERE
home_location = ANY('{Mel''s Hou Rest, Mel''s Lad Rest}')
AND subs_state = 'active';

How to get substring from 4th occurence of a character until the end of given string in PSQL

Example
I have a string...
'/this/is/a/given/string/test.file'.
How can I get substring 'given/string/test.file' in PSQL?
Thank you!
You can use a regular expression
with example(str) as (
values('/this/is/a/given/string/test.file')
)
select regexp_replace(str, '(/.*?){4}', '')
from example;
regexp_replace
------------------------
given/string/test.file
(1 row)
or the function string_to_array():
select string_agg(word, '/' order by ord)
from example,
unnest(string_to_array(str, '/')) with ordinality as u(word, ord)
where ord > 4;
Read also How to find the 3rd occurrence of a pattern on a line.
I dont know how to get the nth occurence of a substring, but for this problem, you can use regular expression. Like this:
select substring('/this/is/a/given/string/test.file' from '/[^/]+/[^/]+/[^/]+/(.*)')
You can improve the regular expression, this is just for demo purpose.

Pattern matching with identical wildcards

I'm working with PostgreSQL and want to know whether you can have a wildcard retain its value.
So for example say I had
select * from tableOne where field like ‘_DEF_’;
Is there a way to get the first and last wildcard to be the exact same character?
So an example matching result could be: ADEFA or ZDEFZ.
You can use a regular expression with a back-reference:
select *
from some_table
where some_column ~* '^(.)DEF(\1)$'
^(.)DEF(\1)$ means: some character at the beginning followed DEF followed by the first character must occur at the end of the string.
The () defines a group and the \1 references the first group (which is the first character in the input sequence in this example)
SQLFiddle example: http://sqlfiddle.com/#!15/d4c4d/1
Use regular expression:
with test as (
select 'xABa' as foo
union select 'xABx'
union select 'xJBx'
)
select * from test
where foo ~* E'^(.)AB\\1$'
Outputs:
foo
------
xABx
(1 row)

handle escape sequence char in DB2

I want to search a column and get values where value containts \ .
I tried select * from "Values" where "ValueName" like '\'. But returns no value.
Also tried like "\" and like'\''%' etc. But no results.
See the DB2 Documentation on the LIKE predicate, in particular the parts about escape expressions.
What you want is
select * from Values where ValueName like '\\%' escape '\'
To give an example of usage:
create table backslash_escape_test
(
backslash_escape_test_column varchar(20)
);
insert into backslash_escape_test(backslash_escape_test_column)
values ('foo\');
insert into backslash_escape_test(backslash_escape_test_column)
values ('no slashes here');
insert into backslash_escape_test(backslash_escape_test_column)
values ('foo\bar');
insert into backslash_escape_test(backslash_escape_test_column)
values ('\bar');
select count(*) from backslash_escape_test where
backslash_escape_test_column like '%\\%' escape '\';
returns 3 (all 3 rows with \ in them).
select count(*) from backslash_escape_test where
backslash_escape_test_column like '\\%' escape '\';
returns 1 (the \bar row).
select * from Values where ValueName like '%\\%'
values is a not so good name because it may be confused with the values keyword
Don't escape it. You just need wildcards around it like this:
select count(*)
from escape_test
where test_column like '%\%'
But, suppose you really do need to escape the slash. Here's a simpler, more straightforward answer:
The escape-expression allows you to specify whatever character for escaping that you wish. So why use a character that you're looking for, thus requiring you to escape it? Use any other character instead. I'll use a plus sign as an example, but it could be a backslash, pound-sign, question-mark, anything other than a character you are looking for or one of the wildcard characters (% or _).
select count(*)
from escape_test
where test_column like '%\%' escape '+';
Now you don't have to add anything into your like-pattern.
To hold myself to the same standard of proof that #Michael demonstrated --
create table escape_test
( test_column varchar(20) );
insert into escape_test
(test_column)
values ('foo\'),
('no slashes here'),
('foo\bar'),
('\bar');
select 'test1' trial, count(*) result
from escape_test
where test_column like '%\%'
UNION
select 'test2', count(*)
from escape_test
where test_column like '%\\%' escape '\'
UNION
select 'test3', count(*)
from escape_test
where test_column like '%\%' escape '+'
;
Which returns the same number of rows for each method:
TRIAL RESULT
----- ------
test1 3
test2 3
test3 3