Note on scalar subquery syntax

Started by Kevin Murphyover 20 years ago6 messagesgeneral
Jump to latest
#1Kevin Murphy
murphy@genome.chop.edu

I thought this might be helpful in the future to other duffers such as
myself.

The following is my big contribution to the documentation of the use of
scalar subqueries ;-):

You have to wrap a scalar subquery in its own parentheses even where you
might think it to be unnecessary, such as when the subquery is the sole
argument to a function.

As an example, I wrote a function to explode, or unpack, the elements of
an array onto separate rows (its return type is SETOF whatever), but it
took me a while to figure out how to use it effectively in queries.

You have to use it like this:

RIGHT--> select * from array_explode((select array_col from table1 where
col2 = 'something'));

Note the "extra" set of parentheses. These are crucial: the syntax is
invalid without these, as in:

WRONG--> select * from array_explode(select array_col from table1 where
col2 = 'something');

And no, as mentioned in many archived list messages, you can NOT do the
following, which is what a lot of people (including me) seem to try first:

WRONG--> select array_explode(array_col) from table1 where col2 =
'something');

(The previous command results in the error message: "set-valued function
called in context that cannot accept a set").

-Kevin Murphy

#2Peter Fein
pfein@pobox.com
In reply to: Kevin Murphy (#1)
Re: Note on scalar subquery syntax

Kevin Murphy wrote:

I thought this might be helpful in the future to other duffers such as
myself.

The following is my big contribution to the documentation of the use of
scalar subqueries ;-):

You have to wrap a scalar subquery in its own parentheses even where you
might think it to be unnecessary, such as when the subquery is the sole
argument to a function.

As an example, I wrote a function to explode, or unpack, the elements of
an array onto separate rows (its return type is SETOF whatever), but it
took me a while to figure out how to use it effectively in queries.

Mind posting it? I know I've had need of such I thing & IIRC others
have asked as well...

#3Martijn van Oosterhout
kleptog@svana.org
In reply to: Kevin Murphy (#1)
Re: Note on scalar subquery syntax

On Wed, Aug 03, 2005 at 09:40:26AM -0400, Kevin Murphy wrote:

You have to wrap a scalar subquery in its own parentheses even where you
might think it to be unnecessary, such as when the subquery is the sole
argument to a function.

It first guess I imagine it is because the syntax becomes ambiguous,
expecially if you have multiple arguments to the function.

Say you a function "func" and your query was: SELECT * FROM x ORDER BY y

Then this isn't parsable obviously:

SELECT func( SELECT * FROM x ORDER BY y, 1, 1 ) )

Since you don't know where the ORDER BY ends and the function list
continues. Adding parenthesis at the appropriate point removes the
ambiguity.

Hope this helps,
--
Martijn van Oosterhout <kleptog@svana.org> http://svana.org/kleptog/

Show quoted text

Patent. n. Genius is 5% inspiration and 95% perspiration. A patent is a
tool for doing 5% of the work and then sitting around waiting for someone
else to do the other 95% so you can sue them.

#4Kevin Murphy
murphy@genome.chop.edu
In reply to: Peter Fein (#2)
Re: Note on scalar subquery syntax

Peter Fein wrote:

Kevin Murphy wrote:

As an example, I wrote a function to explode, or unpack, the elements of
an array onto separate rows (its return type is SETOF whatever), but it
took me a while to figure out how to use it effectively in queries.

Mind posting it? I know I've had need of such I thing & IIRC others
have asked as well...

I'm no expert, but per Peter's request, here is a generic
array-unpacking function that works in PostgreSQL 8.0. It can't be
invoked if the argument doesn't have an explicit type. I.e. you would
have to use it as: "select * from
array_explode_generic('{apple,banana,cherry}'::text[]);" or "select *
from array_explode_generic('{1,2,3}'::integer[]);".

CREATE OR REPLACE FUNCTION array_explode(an_array anyarray) RETURNS
SETOF anyelement AS $$
DECLARE
idx integer;
BEGIN

FOR idx IN 1 .. ARRAY_UPPER(an_array, 1) LOOP
RETURN NEXT an_array[idx];
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;

I would imagine that a type-specific version would be faster. For that,
replace "anyarray" with, e.g. "integer[]", and "anyelement" with, e.g.
"integer".

-Kevin Murphy

#5Kevin Murphy
murphy@genome.chop.edu
In reply to: Martijn van Oosterhout (#3)
Re: Note on scalar subquery syntax

Martijn van Oosterhout wrote:

On Wed, Aug 03, 2005 at 09:40:26AM -0400, Kevin Murphy wrote:

You have to wrap a scalar subquery in its own parentheses even where you
might think it to be unnecessary, such as when the subquery is the sole
argument to a function.

It first guess I imagine it is because the syntax becomes ambiguous,
expecially if you have multiple arguments to the function.

Thanks, Martijn. Yes, I don't consider it to be entirely unreasonable;
it was just surprising and strange-looking to me.

-Kevin

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Kevin Murphy (#4)
Re: Note on scalar subquery syntax

Kevin Murphy <murphy@genome.chop.edu> writes:

I'm no expert, but per Peter's request, here is a generic
array-unpacking function that works in PostgreSQL 8.0.
[snip]

I would imagine that a type-specific version would be faster.

No, actually it'd be exactly the same. What happens under the hood with
a plpgsql "anyarray" function is that Postgres instantiates a copy for
each specific datatype you call it with during the life of your session.
So there's no real point in doing the same thing manually.

regards, tom lane