I have a list of Integer objects in my Spring Boot program that I want to use as a reference to update a table, only setting a column to a certain value for records with an id found in this list. Because of the potential length of the list, I want to avoid using the IN clause, as this will likely result in a SQL Server error for too many parameters.
The solution that I am thinking of involves a query similar to the following:
WITH ids as (select * from <list of Integers> as pool(num))
update t set t.column = :value from <table> t, ids where t.id = ids.num
The problem that I see with this is wrapping each Integer in the list in VALUE(), ie:
WITH ids as (select * from value(1),value(2),...etc)
While it seems that I could this query string programmatically by iterating over the list in Java, I would really like to avoid doing so if possible. I did try searching for a solution, but could not find quite what I was looking for. Is there a solution for this?
You are constructing the query anyway, so I personally don't see an issue with constructing a values clause. However, you can also parse a string:
update t
set t.column = :value
from <table> t,
where t.id in (select * from string_split(#ids, ','));
Note: You may need to be careful about type conversion, so if id is an integer:
update t
set t.column = :value
from <table> t,
where t.id in (select try_convert(int, value) from string_split(#ids, ','));
Related
I sorted the values of a column, now i want remove the brackets inside {} bracktes.
The following code is for the sorting and updating it in:
FOR _reference, _val IN select reference, categories from responses
LOOP
_array = (select (array_agg(p order by p.a )) from (select unnest(_val::text[]) as a) as p);
update responses SET categories = _array where reference = _reference;
END LOOP;
the output of the categories in the table looks like:
{(DSM),(Post)}
I need to that the output looks like:
{DSM,Post}
You are mixing table aliases and column aliases which is the root of your problem.
If you simplify your expression by removing unnecessary levels of nesting and parentheses, things work just fine:
(select array_agg(p.a order by p.a) from unnest(_val::text[]) as p(a))
However you don't need an inefficient PL/pgSQL loop for this. You can do this in a single UPDATE statement.
update responses
set _val = (select array_agg(p.a order by p.a) from unnest(_val) as p(a))
Or slightly more efficient without array_agg()
update responses
set _val = array(select p.a from unnest(_val) as p(a) order by p.a)
_val is apparently an array column, so the cast ::text[] seems unnecessary.
I know this isn't valid SQL, but I'd like to do something like:
SELECT items.{SELECT items.preferred_column}
To elaborate, to achieve what I'm trying to achieve, I could write a long case when statement:
SELECT
CASE WHEN items.preferred_column = "column_a" THEN items.column_a
CASE WHEN items.preferred_column = "column_b" THEN items.column_b
CASE WHEN items.preferred_column = "column_c" THEN items.column_c
... and so on...
But that seems wrong. I would prefer to write a query that looks at the value of items.preferred_column and loads that column.
Is this possible?
My use case involves an Active Record (the ORM for Rails) query, which limits me. I'm not able to use "INTO" for example.
Doing this without creating a SQL function would preferred, though if it's not possible without creating a SQL function that would be good to know.
Thanks in advance for lending your expertise!
You can try transforming the table rows with row_to_json() and then using json_each(), you can join the resultant "key" field on the preferred_column:
WITH CTE AS (
SELECT
row_to_json(Z.*)::jsonb as rcr,
row_number() over(partition by null order by <whatever comparator clause>) as rn,
Z.*
FROM items Z)
SELECT b.value, a.*
FROM CTE a, jsonb_each(rcr) b, CTE c
WHERE c.rn=a.rn AND b.key = ( c.preferred_column )
Note that this essentially operates as a quasi-pivot, so you'll need to maintain an index (the row_number invocation) to self-join the table when extracting the appropriate key-value pairs from jsonb_each's set-return. Casting to jsonb will be helpful in that the binary form will alphabetize the key-value pairs by key order within the object itself.
If you need to get the resultant value as a text string instead of a json primitive, you can do
b.value #>>'{}'
instead of using jsonb_each_text(), which will preserve any json columns.
I'm just taking a look at the following query
select * from tablename
where id like '%%';
So that it can handle parameters to include all of the data or filtered data like bellow
select * from tablename
where id like '%1%';
Which is fine for most parameters I use but this seems wrong for an ID because it will return all data that has IDs containing 1 which I don't want
To get around this I can only append the where clause if the ID is given but that seems like a pain in the butt
Is it possible to use a different type of where clause so that a wildcard can be used in a where equals clause instead of a where like clause, example
select * from tablename
where id = '*';
So that the same query can be used to return all or filtered data? Pass parameter '*' for all or parameter '1' for ID 1 specifically
(I'm not sure if it matters for this case but I'm using PostgreSQL 9.6.12 in this example)
This would often be expressed as:
where (id = :id or :id is null)
null is the "magic" value that represents all rows.
I'm trying to write a SP that will allow users to search on multiple name strings, but supports LIKE functionality. For example, the user's input might be a string 'Scorsese, Kaurismaki, Tarkovsky'. I use a split function to turn that string into a table var, with one column, as follows:
part
------
Scorsese
Kaurismaki
Tarkovsky
Then, normally I would return any values from my table matching any of these values in my table var, with an IN statement:
select * from myTable where lastName IN (select * from #myTableVar)
However, this only returns exact matches, and I need to return partial matches. I'm looking for something like this, but that would actually compile:
select * from myTable where CONTAINS(lastName, select * from #myTableVar)
I've found other questions where it's made clear that you can't combine LIKE and IN, and it's recommended to use CONTAINS. My specific question is, is it possible to combine CONTAINS with a table list of values, as above? If so, what would that syntax look like? If not, any other workarounds to achieve my goal?
I'm using SQL Server 2016, if it makes any difference.
You can use EXISTS
SELECT * FROM myTable M
WHERE
EXISTS( SELECT * FROM #myTableVar V WHERE M.lastName like '%'+ V.part +'%' )
Can your parser built the entire statement? Will that get you what you want?
select *
from myTable
where CONTAINS
(lastName,
'"Scorsese" OR "Kaurismaki" OR "Tarkovsky"'
)
This can be done using CHARINDEX function combined with EXISTS:
select *
from myTable mt
where exists(select 1 from #myTableVar
where charindex(mt.lastName, part) > 0
or charindex(part, mt.lastName) > 0)
You might want to omit one of the conditions in the inner query, but I think this is what you want.
I'm having problems finding the answer here, on google or in the docs ...
I need to do a case insensitive select against an array type.
So if:
value = {"Foo","bar","bAz"}
I need
SELECT value FROM table WHERE 'foo' = ANY(value)
to match.
I've tried lots of combinations of lower() with no success.
ILIKE instead of = seems to work but I've always been nervous about LIKE - is that the best way?
One alternative not mentioned is to install the citext extension that comes with PostgreSQL 8.4+ and use an array of citext:
regress=# CREATE EXTENSION citext;
regress=# SELECT 'foo' = ANY( '{"Foo","bar","bAz"}'::citext[] );
?column?
----------
t
(1 row)
If you want to be strictly correct about this and avoid extensions you have to do some pretty ugly subqueries because Pg doesn't have many rich array operations, in particular no functional mapping operations. Something like:
SELECT array_agg(lower(($1)[n])) FROM generate_subscripts($1,1) n;
... where $1 is the array parameter. In your case I think you can cheat a bit because you don't care about preserving the array's order, so you can do something like:
SELECT 'foo' IN (SELECT lower(x) FROM unnest('{"Foo","bar","bAz"}'::text[]) x);
This seems hackish to me but I think it should work
SELECT value FROM table WHERE 'foo' = ANY(lower(value::text)::text[])
ilike could have issues if your arrays can have _ or %
Note that what you are doing is converting the text array to a single text string, converting it to lower case, and then back to an array. This should be safe. If this is not sufficient you could use various combinations of string_to_array and array_to_string, but I think the standard textual representations should be safer.
Update building on subquery solution below, one option would be a simple function:
CREATE OR REPLACE FUNCTION lower(text[]) RETURNS text[] LANGUAGE SQL IMMUTABLE AS
$$
SELECT array_agg(lower(value)) FROM unnest($1) value;
$$;
Then you could do:
SELECT value FROM table WHERE 'foo' = ANY(lower(value));
This might actually be the best approach. You could also create GIN indexes on the output of the function if you want.
Another alternative would be with unnest()
WITH tbl AS (SELECT 1 AS id, '{"Foo","bar","bAz"}'::text[] AS value)
SELECT value
FROM (SELECT id, value, unnest(value) AS val FROM tbl) x
WHERE lower(val) = 'foo'
GROUP BY id, value;
I added an id column to get exactly identical results - i.e. duplicate value if there are duplicates in the base table. Depending on your circumstances, you can probably omit the id from the query to collapse duplicates in the results or if there are no dupes to begin with. Also demonstrating a syntax alternative:
SELECT value
FROM (SELECT value, lower(unnest(value)) AS val FROM tbl) x
WHERE val = 'foo'
GROUP BY value;
If array elements are unique within arrays in lower case, you don't even need the GROUP BY, since every value can only match once.
SELECT value
FROM (SELECT value, lower(unnest(value)) AS val FROM tbl) x
WHERE val = 'foo';
'foo' must be lower case, obviously.
Should be fast.
If you want that fast wit a big table, I would create a functional GIN index, though.
my solution to exclude values using a sub select...
and groupname not ilike all (
select unnest(array[exceptionname||'%'])
from public.group_exceptions
where ...
and ...
)
Regular expression may do the job for most cases
SELECT array_to_string('{"a","b","c"}'::text[],'|') ~* ANY('{"A","B","C"}');
I find creating a custom PostgreSQL function works best for me
CREATE OR REPLACE FUNCTION lower(text_array text[]) RETURNS text[] AS
$BODY$
SELECT (lower(text_array::text))::text[]
$BODY$
LANGUAGE SQL IMMUTABLE;