Thread: Arrays and ANY problem
Perhaps someone can guide me here as I'm having a "moment". :)
Not sure why I am getting 0 rows returned here:
db=# \d table_name_ds_tmp
Column | Type | Modifiers
------------+-------------------+-----------
categoryid | bigint |
name | character varying |
db=# select * from table_name_ds_tmp;
categoryid | name
------------+-------
100 | one
200 | two
300 | three
400 | four
(4 rows)
db=# select name from table_name_ds_tmp where ARRAY[categoryid] = ANY ( select string_to_array( '200,400', ',')::bigint[] );
name
------
Column | Type | Modifiers
------------+-------------------+-----------
categoryid | bigint |
name | character varying |
db=# select * from table_name_ds_tmp;
categoryid | name
------------+-------
100 | one
200 | two
300 | three
400 | four
(4 rows)
db=# select name from table_name_ds_tmp where ARRAY[categoryid] = ANY ( select string_to_array( '200,400', ',')::bigint[] );
name
------
(0 rows)
I would expect, and am in need of, the query returning these 2 rows,
"two"
"four"
which I plan to aggregate together later on with array_to_string.
Basically I hope to take a string and substitute the values in it with their associated values.
Other incantations and permutations just seem to give me various error messages.
Thanks for any help.
db=# select name from table_name_ds_tmp where ARRAY[categoryid] = ANY ( select string_to_array( '200,400', ',')::bigint[] );
Using either of the below instead, I get the proper result. Why doesn't ANY work? I do not know.
select name from table_name_ds_tmp where ARRAY[categoryid] <@ ( select (string_to_array( '200,400', ','))::bigint[] );
select name from table_name_ds_tmp where categoryid = ANY ( ARRAY[ 200, 400]::BIGINT[] );
I used-
drop table if exists pg_temp.table_name_ds_tmp;
create temp table table_name_ds_tmp AS(
SELECT 100::BIGINT AS categoryid, 'one'::VARCHAR AS name UNION ALL
SELECT 200::BIGINT, 'two'::VARCHAR UNION ALL
SELECT 300::BIGINT, 'three'::VARCHAR UNION ALL
SELECT 400::BIGINT, 'four'::VARCHAR
);
drop table if exists pg_temp.table_name_ds_tmp;
create temp table table_name_ds_tmp AS(
SELECT 100::BIGINT AS categoryid, 'one'::VARCHAR AS name UNION ALL
SELECT 200::BIGINT, 'two'::VARCHAR UNION ALL
SELECT 300::BIGINT, 'three'::VARCHAR UNION ALL
SELECT 400::BIGINT, 'four'::VARCHAR
);
> On 25 Sep 2019, at 22:25, David Salisbury <dsalis@ucar.edu> wrote: > db=# select name from table_name_ds_tmp where ARRAY[categoryid] = ANY ( select string_to_array( '200,400', ',')::bigint[]); > name > ------ > (0 rows) You are comparing two arrays for equality. Since the left-hand array has only 1 item and the right-hand one has two, there’snot much equality between them. You probably meant: select name from table_name_ds_tmp where categoryid = ANY ( select string_to_array( '200,400', ',')::bigint[] ); Alban Hertroys -- There is always an exception to always.
> On 25 Sep 2019, at 22:50, Alban Hertroys <haramrae@gmail.com> wrote: > > >> On 25 Sep 2019, at 22:25, David Salisbury <dsalis@ucar.edu> wrote: >> db=# select name from table_name_ds_tmp where ARRAY[categoryid] = ANY ( select string_to_array( '200,400', ',')::bigint[]); >> name >> ------ >> (0 rows) > > You are comparing two arrays for equality. Since the left-hand array has only 1 item and the right-hand one has two, there’snot much equality between them. > > You probably meant: > select name from table_name_ds_tmp where categoryid = ANY ( select string_to_array( '200,400', ',')::bigint[] ); Or rather: select name from table_name_ds_tmp where categoryid = ANY ( string_to_array( '200,400', ',')::bigint[] ); Alban Hertroys -- If you can't see the forest for the trees, cut the trees and you'll find there is no forest.
Thanks,
Unfortunately I believe I need to include a postgres module to get the "<@" operator, which I have no power to do. This is what I get with that operator..
ERROR: operator does not exist: bigint <@ bigint
The second query does work, but in the end I'll need to have a select in that area to pick out my numbers, can't hard code it, and that seems to be what screws my query up, the select, and that makes no sense.
Here's what happens without the ARRAY wrapping around categoryid, as it your second thought...
select name from table_name_ds_tmp where categoryid = ANY ( select string_to_array( '200,400', ',')::bigint[] );
ERROR: operator does not exist: bigint = bigint[]
ERROR: operator does not exist: bigint = bigint[]
At least it runs with ARRAY[categoryid], it just doesn't return anything. :-((
On Wed, Sep 25, 2019 at 2:48 PM Michael Lewis <mlewis@entrata.com> wrote:
db=# select name from table_name_ds_tmp where ARRAY[categoryid] = ANY ( select string_to_array( '200,400', ',')::bigint[] );Using either of the below instead, I get the proper result. Why doesn't ANY work? I do not know.select name from table_name_ds_tmp where ARRAY[categoryid] <@ ( select (string_to_array( '200,400', ','))::bigint[] );select name from table_name_ds_tmp where categoryid = ANY ( ARRAY[ 200, 400]::BIGINT[] );I used-
drop table if exists pg_temp.table_name_ds_tmp;
create temp table table_name_ds_tmp AS(
SELECT 100::BIGINT AS categoryid, 'one'::VARCHAR AS name UNION ALL
SELECT 200::BIGINT, 'two'::VARCHAR UNION ALL
SELECT 300::BIGINT, 'three'::VARCHAR UNION ALL
SELECT 400::BIGINT, 'four'::VARCHAR
);
On Wed, Sep 25, 2019 at 3:08 PM David Salisbury <dsalis@ucar.edu> wrote:
Thanks,Unfortunately I believe I need to include a postgres module to get the "<@" operator, which I have no power to do. This is what I get with that operator..select name from table_name_ds_tmp where categoryid <@ ANY ( ARRAY[ 200, 400]::BIGINT[] );
ERROR: operator does not exist: bigint <@ bigint
The "<@" operator is standard...you were even provided an example of how to use it. That its doesn't work when you do something different isn't surprising.
The second query does work, but in the end I'll need to have a select in that area to pick out my numbers, can't hard code it, and that seems to be what screws my query up, the select, and that makes no sense.
Then how about providing what you will eventually need so people aren't wasting their time with stuff you won't be able to use.
There are two "ANY" constructs documented. One covers a subquery and one encapsulates an array. The presence of "select" forces the subquery interpretation even if the select just happens to be providing a set of arrays (a set of cardinality one).
Here's what happens without the ARRAY wrapping around categoryid, as it your second thought...select name from table_name_ds_tmp where categoryid = ANY ( select string_to_array( '200,400', ',')::bigint[] );
ERROR: operator does not exist: bigint = bigint[]At least it runs with ARRAY[categoryid], it just doesn't return anything. :-(
This is all documented and the specific reason this doesn't match has been previously explained in this thread.
See:
compared to
David J.
Alban Hertroys <haramrae@gmail.com> writes: >> On 25 Sep 2019, at 22:50, Alban Hertroys <haramrae@gmail.com> wrote: >> You probably meant: >> select name from table_name_ds_tmp where categoryid = ANY ( select string_to_array( '200,400', ',')::bigint[] ); > Or rather: > select name from table_name_ds_tmp where categoryid = ANY ( string_to_array( '200,400', ',')::bigint[] ); Yeah, this is fairly confusing, because there are multiple different features with barely distinguishable syntaxes here. You can do value = ANY (SELECT ...) which compares "value" to each row of the sub-SELECT result (and the sub-SELECT had better return one column, of a type comparable to "value"). Or you can do value = ANY (array-expression) which compares "value" to each element of the array value (which had better have elements of a type comparable to "value"). What you can't do is generate the array value from a sub-select, because that will be taken as being an instance of the first feature. David didn't say what his real problem was, but I'd suggest that making his sub-select return a rowset result rather than an array result might be the best way to resolve things. It's more SQL-y, for sure. regards, tom lane
I didn't specify the real problem as it's all wrapped up in layers and I didn't want to post a "can someone write the query for me". The real problem was I have a table with a string holding comma separated numbers, and needed to go to a lookup table and replace each of those numbers with it's correlated value. So '12,2,10' gets converted to 'twelve,two,ten'.
Tom's "I'd suggest that making his sub-select return a rowset result rather than an array" was spot on and lead me to "unnest". For my posted problem this was the simple solution. Sorry to narrow things down to my specific array method.
select name from table_name_ds_tmp where categoryid = ANY ( select unnest(string_to_array( '200,400', ',')::bigint[]) );
Thanks everyone for the help!
-ds
On Wed, Sep 25, 2019 at 4:38 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Alban Hertroys <haramrae@gmail.com> writes:
>> On 25 Sep 2019, at 22:50, Alban Hertroys <haramrae@gmail.com> wrote:
>> You probably meant:
>> select name from table_name_ds_tmp where categoryid = ANY ( select string_to_array( '200,400', ',')::bigint[] );
> Or rather:
> select name from table_name_ds_tmp where categoryid = ANY ( string_to_array( '200,400', ',')::bigint[] );
Yeah, this is fairly confusing, because there are multiple different
features with barely distinguishable syntaxes here. You can do
value = ANY (SELECT ...)
which compares "value" to each row of the sub-SELECT result (and the
sub-SELECT had better return one column, of a type comparable to
"value"). Or you can do
value = ANY (array-expression)
which compares "value" to each element of the array value (which had
better have elements of a type comparable to "value"). What you
can't do is generate the array value from a sub-select, because that
will be taken as being an instance of the first feature.
David didn't say what his real problem was, but I'd suggest that
making his sub-select return a rowset result rather than an array
result might be the best way to resolve things. It's more SQL-y,
for sure.
regards, tom lane
>>>>> "David" == David Salisbury <dsalis@ucar.edu> writes: David> I didn't specify the real problem as it's all wrapped up in David> layers and I didn't want to post a "can someone write the query David> for me". The real problem was I have a table with a string David> holding comma separated numbers, and needed to go to a lookup David> table and replace each of those numbers with it's correlated David> value. So '12,2,10' gets converted to 'twelve,two,ten'. David> Tom's "I'd suggest that making his sub-select return a rowset David> result rather than an array" was spot on and lead me to David> "unnest". For my posted problem this was the simple solution. David> Sorry to narrow things down to my specific array method. David> select name from table_name_ds_tmp where categoryid = ANY ( select David> unnest(string_to_array( '200,400', ',')::bigint[]) ); This won't necessarily preserve the order of elements (it might sometimes look like it does, but it's fooling you). It also won't handle duplicate numbers. The right solution that does preserve order would be: select name from unnest(string_to_array( '200,400', ',')::bigint[]) with ordinality as u(id,ord) join table_name_ds_tmp t on (t.category_id=u.id) order by u.ord; (wrap an ARRAY( ) around that if you need the result as a single array rather than as rows, or use string_agg(name, ',' order by ord) if you want a comma-separated string result) regexp_split_to_table might be a better method than unnest/string_to_array. -- Andrew (irc:RhodiumToad)