How to multi-split a string (WHERE IN query) - sql

I am passing a string as param (via massivejs) into my query. The string is formatted as: param = 'red, blue, green'. The param itself does not have a fixed length (',' being the delimiter) as it is populated through what the user sends in (but is maxed out at 10 elements).
How would I break the string down into individual strings inside my query?
Eg of what I am trying to do:
SELECT * FROM table
WHERE name IN (param);
I know this works but is very very crude:
SELECT * FROM table
WHERE name IN (split_part(param, ',', 1), split_part(param, ',', 2) .......)) -- keep going.
Essentially I want to have ('red', 'blue', 'green' ....) inside the IN parenthesis. Is there a nicer way of accomplishing this?

You could use the string_to_array function to split the string to an array and then use the any function to check if your element is contained in it:
SELECT *
FROM mytable
WHERE name = ANY(STRING_TO_ARRAY(param, ','));

Related

Query condition where a value is in a comma separated string

I have a table in which one of the table columns third_row stores a comma-separated list of numbers as a string but when its value is A then it means a combination of all the possible numbers. How do I approach this so that the query returns all the rows that have the third_row as A and the rest where third_row is equal to one of the values in the comma-separated string?
For reference, here is the format of the table:
first_row
second_row
third_row
0028001070200
50
A
0049048000701
51
01,04,02,31,
I have also tried this query but no luck:
SELECT
sds.scheme_code,
rs.scheme_name
FROM
trea.salary_deduction_schemes sds
LEFT JOIN
trea.receipt_schemes rs
ON sds.scheme_code = rs.scheme_code
WHERE sds.list_object_head = 'A'
OR 16 IN(regexp_split_to_table(sds.list_object_head, E','))
Your method almost works:
WHERE sds.list_object_head = 'A' OR
16 IN (SELECT val::int
FROM regexp_split_to_table(sds.list_object_head, E',') val
)
You can also use string matching:
WHERE ',' || sds.list_object_head || ',' LIKE '%,' || 16 || ',%'
Or you could convert to an array and use array operations.
I would strongly suggest that find a representation other than strings for storing integer values -- preferably another table or perhaps an array.
You can convert the list to an array and use the = any operator:
WHERE sds.list_object_head = 'A'
OR 16 = any(string_to_array(trim(',' from sds.list_object_head), ',')::int[])
The trim() is necessary to get rid of the trailing , that would result in an empty string after applying string_to_array() and that in turn would result in a casting error as an empty string isn't a valid integer.
This is probably a bit faster than using a regex and unnesting the array.

How to generate random string for all rows in postgres

I have foo table and would like to set bar column to a random string. I've got the following query:
update foo
set bar = select string_agg(substring('0123456789bcdfghjkmnpqrstvwxyz', round(random() * 30)::integer, 1), '')
from generate_series(1, 9);
But it generates the random string once and reuse it for all rows. How can I make it to generate one random string for each row?
I know I can make it a function like this:
create function generate_bar() returns text language sql as $$
select string_agg(substring('0123456789bcdfghjkmnpqrstvwxyz', round(random() * 30)::integer, 1), '')
from generate_series(1, 9)
$$;
and then call the function in the update query. But I'd prefer to do it without a function.
The problem is that the Postgres optimizer is just too smart and deciding that it can execute the subquery only once for all rows. Well -- it is really missing something obvious -- the random() function makes the subquery volatile so this is not appropriate behavior.
One way to get around this is to use a correlated subquery. Here is an example:
update foo
set bar = array_to_string(array(select string_agg(substring('0123456789bcdfghjkmnpqrstvwxyz', round(random() * 30)::integer, 1), '')
from generate_series(1, 9)
where foo.bar is distinct from 'something'
), '');
Here is a db<>fiddle.
For a random mixed-case numeric-inclusive string containing up to 32 characters use:
UPDATE "foo" SET "bar"= substr(md5(random()::text), 0, XXX);
and replace XXX with the length of desired string plus one.
To replace all with length 32 strings, Example:
UPDATE "foo" SET "bar"= substr(md5(random()::text), 0, 33);
14235ccd21a408149cfbab0a8db19fb2 might be a value that fills one of the rows. Each row will have a random string but not guaranteed to be unique.
For generating strings with more than 32 characters
Just combine the above with a CONCAT

Use of substring in SQL

My query is the following:
SELECT id, category FROM table1
This returns the following rows:
ID|category
1 |{IN, SP}
2 |
3 |{VO}
Does anyone know how i can remove the first char and last char of the string in PostgreSQL, so it removes: {}?
Not sure, what you mean with "foreign column", but as the column is an array, the best way to deal with that is to use array_to_string()
SELECT id, array_to_string(category, ',') as category
FROM table1;
The curly braces are not part of the stored value. This is just the string representation of an array that is used to display it.
Either using multiple REPLACE functions.
SELECT id, REPLACE(REPLACE(category, '{', ''), '}', '')
FROM table1
Or using a combination of the SUBSTRING, LEFT & LENGTH functions
SELECT id, LEFT(SUBSTRING(category, 2, 999),LENGTH(SUBSTRING(category, 2, 999)) - 1)
FROM table1
Or just SUBSTRING and LENGTH
SELECT id, SUBSTRING(category, 2, LENGTH(category)-2)
FROM table1
You could replace the {} with an empty string
SELECT id, replace(replace(category, '{', ''), '}', '') FROM table1
select id,
substring(category,charindex('{',category,len(category))+2,len(category)-2)
from table1;
select id
,left(right(category,length(category)-1),length(category)-2) category
from boo
select id
,trim(both '{}' from category)
from boo
Trim():
Remove the longest string containing only the characters (a space by
default) from the start/end/both ends of the string
The syntax for the replace function in PostgreSQL is:
replace( string, from_substring, to_substring )
Parameters or Arguments
string
The source string.
from_substring
The substring to find. All occurrences of from_substring found within string are replaced with to_substring.
to_substring
The replacement substring. All occurrences of from_substring found within string are replaced with to_substring.
UPDATE dbo.table1
SET category = REPLACE(category, '{', '')
WHERE ID <=3
UPDATE dbo.table1
SET category = REPLACE(category, '}', '')
WHERE ID <=3

How to use regexp_substr() with group of delimiter characters?

I have a string something like this 'SERO02~~~NA_#ERO5'. I need to sub string it using delimiter ~~~. So can get SERO02 and NA_#ERO5 as result.
I create an regex experession like this:
select regexp_substr('SERO02~~~NA_#ERO5' ,'[^~~~]+',1,2) from dual;
It worked fine and returns : NA_#ERO5
But if I change the string to ERO02~NA_#ERO5 the result is still same.
But I expect the expression to return nothing since delimiter ~~~ is not found in that string. Can someone help me out to create correct expression?
[^~~~] matches a single character that is not one of the characters following the caret in the square brackets. Since all those characters are identical then [^~~~] is the same as [^~].
You can match it using:
SELECT REGEXP_SUBSTR(
'SERO02~~~NA_#ERO5',
'~~~(.*?)(~~~|$)',
1,
1,
NULL,
1
)
FROM DUAL;
Which will match ~~~ then store zero-or-more characters in a capture group (the round brackets () indicates a capture group) until it finds either ~~~ or the end-of-string. It will then return the first capture group.
You can do it without regular expressions, with a bit of logics:
with test(text) as ( select 'SERO02~~~NA_#ERO5' from dual)
select case
when instr(text, '~~~') != 0 then
substr(text, instr(text, '~~~') + 3)
else
null
end
from test
This will give the part of the string after '~~~', if it exists, null otherwise.
You can edit the ELSE part to get what you need when the input string does not contain '~~~'.
Even using regexp,to match the string '~~~', you need to write it exactly, without []; the [] is used to list a set of characters, so [aaaaa] is exactly the same than [a],while [abc] means 'a' OR 'b' OR 'c'.
With regexp, even if not necessary, one way could be the following:
substr(regexp_substr(text, '~~~.*'), 4)
In case you want all elements. Handles NULL elements too:
SQL> with tbl(str) as (
select 'SERO02~~~NA_#ERO5' from dual
)
select regexp_substr(str, '(.*?)(~~~|$)', 1, level, null, 1) element
from tbl
connect by level <= regexp_count(str, '~~~') + 1;
ELEMENT
-----------------
SERO02
NA_#ERO5
SQL>

sql in clause doesn't work

I have a table with a column ancestry holding a list of ancestors formatted like this "1/12/45". 1 is the root, 12 is children of 1, etc...
I need to find all the records having a specific node/number in their ancestry list. To do so, I wrote this sql statement:
select * from nodes where 1 in (nodes.ancestry)
I get following error statement: operator does not exist: integer = text
I tried this as well:
select * from nodes where '1' in (nodes.ancestry)
but it only returns the records having 1 in their ancestry field. Not the one having for instance 1/12/45
What's wrong?
Thanks!
This sounds like a job for LIKE, not IN.
If we assume you want to search for this value in any position, and then we might try:
select * from nodes where '/' + nodes.ancestry + '/' like '%/1/%'
Note that exact syntax for string concatenation varies between SQL products. Note that I'm prepending and appending to the ancestry column so that we don't have to treat the first/last items in the list differently than middle items. Note also that we surround the 1 with /s, so that we don't get false matches for e.g. with /51/ or /12/.
In MySQL you could write:
SELECT * FROM nodes
WHERE ancestry = '1'
OR LEFT(ancestry, 2) = '1/'
OR RIGHT(ancestry, 2) = '/1'
OR INSTR(ancestry, '/1/') > 0
The in operator expects a comma separated list of values, or a query result, i.e.:
... in (1,2,3,4,5)
or:
... in (select id from SomeOtherTable)
What you need to do is to create a string from the number, so that you can look for it in the other string.
Just looking for the string '1' in the ancestry list would give false positives, as it would find it in the string '2/12/45'. You need to add the separator to the beginning and the end of both strings, so that you look for a string like '/1/' in a string like '/1/12/45/':
select * from nodes
where charindex('/' + convert(varchar(50), 1) + '/', '/' + nodes.ancestry + '/') <> 0