Thread: plpgsql question
I'm trying to do a simple SELECT * in plpgsql that returns a set of records as a row w/ columns, not a row into a variable, w/ some conditionals.
The function below is semi-pseudo with what I'm trying to... If anyone could give me an example that works by returning it as a resultset maintaining the columns, that would be awesome and I could take it from there.
I've read the pl/pgsql section of the docs and the Douglas book but I'm still confused on this issue...
Thanks
create or replace function getrecord(int,text) RETURNS SETOF records as $$
DECLARE
-- event := rows to return from the table below
BEGIN
event := SELECT * FROM my_tbl
WHERE 1 = 1
and my_tbl_id IN (0$1) ||
' IF $2 IS NOT NULL THEN' || and username = $2 || 'END IF;'
; -- end sql statement
RETURN event;
END;
$$ LANGUAGE plpgsql;
The function below is semi-pseudo with what I'm trying to... If anyone could give me an example that works by returning it as a resultset maintaining the columns, that would be awesome and I could take it from there.
I've read the pl/pgsql section of the docs and the Douglas book but I'm still confused on this issue...
Thanks
create or replace function getrecord(int,text) RETURNS SETOF records as $$
DECLARE
-- event := rows to return from the table below
BEGIN
event := SELECT * FROM my_tbl
WHERE 1 = 1
and my_tbl_id IN (0$1) ||
' IF $2 IS NOT NULL THEN' || and username = $2 || 'END IF;'
; -- end sql statement
RETURN event;
END;
$$ LANGUAGE plpgsql;
Yahoo! DSL Something to write home about. Just $16.99/mo. or less
Assuming records is the name of a table... create or replace function getrecord(int,text) RETURNS SETOF records as $$ DECLARE row records%rowtype; BEGIN FOR row IN SELECT * FROM my_tbl WHERE ... LOOP RETURN NEXT row; END LOOP; RETURN; END; $$ LANGUAGE plpgsql; On 1/5/06, Matthew Peter <survivedsushi@yahoo.com> wrote: > I'm trying to do a simple SELECT * in plpgsql that returns a set of records > as a row w/ columns, not a row into a variable, w/ some conditionals. > > The function below is semi-pseudo with what I'm trying to... If anyone > could give me an example that works by returning it as a resultset > maintaining the columns, that would be awesome and I could take it from > there. > > I've read the pl/pgsql section of the docs and the Douglas book but I'm > still confused on this issue... > > Thanks > > create or replace function getrecord(int,text) RETURNS SETOF records as $$ > DECLARE > -- event := rows to return from the table below > > BEGIN > event := SELECT * FROM my_tbl > WHERE 1 = 1 > and my_tbl_id IN (0$1) || > ' IF $2 IS NOT NULL THEN' || and username = $2 || 'END IF;' > ; -- end sql statement > > RETURN event; > > END; > $$ LANGUAGE plpgsql; > > > > > ________________________________ > Yahoo! DSL Something to write home about. Just $16.99/mo. or less > >
On 1/5/06, Matthew Peter wrote:
> I'm trying to do a simple SELECT * in plpgsql that returns a set of records
> as a row w/ columns, not a row into a variable, w/ some conditionals.
>
> The function below is semi-pseudo with what I'm trying to... If anyone
> could give me an example that works by returning it as a resultset
> maintaining the columns, that would be awesome and I could take it from
> there.
>
> I've read the pl/pgsql section of the docs and the Douglas book but I'm
> still confused on this issue...
>
> Thanks
>
> create or replace function getrecord(int,text) RETURNS SETOF records as $$
> DECLARE
> -- event := rows to return from the table below
>
> BEGIN
> event := SELECT * FROM my_tbl
> WHERE 1 = 1
> and my_tbl_id IN (0$1) ||
> ' IF $2 IS NOT NULL THEN' || and username = $2 || 'END IF;'
> ; -- end sql statement
>
> RETURN event;
>
> END;
> $$ LANGUAGE plpgsql;
>
Pandurangan R S <pandurangan.r.s@gmail.com> wrote:
Thanks for the reply.
Is it possible to skip the loop and just return all records in a single query and shove all those rows into a table variable?
Also, the WHERE part is also important cause I'm not sure i got that part right? Would this call for EXECUTE or will it be okay and be planned the first time by the query planner?
> I'm trying to do a simple SELECT * in plpgsql that returns a set of records
> as a row w/ columns, not a row into a variable, w/ some conditionals.
>
> The function below is semi-pseudo with what I'm trying to... If anyone
> could give me an example that works by returning it as a resultset
> maintaining the columns, that would be awesome and I could take it from
> there.
>
> I've read the pl/pgsql section of the docs and the Douglas book but I'm
> still confused on this issue...
>
> Thanks
>
> create or replace function getrecord(int,text) RETURNS SETOF records as $$
> DECLARE
> -- event := rows to return from the table below
>
> BEGIN
> event := SELECT * FROM my_tbl
> WHERE 1 = 1
> and my_tbl_id IN (0$1) ||
> ' IF $2 IS NOT NULL THEN' || and username = $2 || 'END IF;'
> ; -- end sql statement
>
> RETURN event;
>
> END;
> $$ LANGUAGE plpgsql;
>
Pandurangan R S <pandurangan.r.s@gmail.com> wrote:
Assuming records is the name of a table...
create or replace function getrecord(int,text) RETURNS SETOF records as $$
DECLARE
row records%rowtype;
BEGIN
FOR row IN SELECT * FROM my_tbl
WHERE ...
LOOP
RETURN NEXT row;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
Thanks for the reply.
Is it possible to skip the loop and just return all records in a single query and shove all those rows into a table variable?
Also, the WHERE part is also important cause I'm not sure i got that part right? Would this call for EXECUTE or will it be okay and be planned the first time by the query planner?
Yahoo! Photos
Ring in the New Year with Photo Calendars. Add photos, events, holidays, whatever.
On Thu, Jan 05, 2006 at 12:50:34AM -0800, Matthew Peter wrote: > Is it possible to skip the loop and just return all records in a > single query and shove all those rows into a table variable? Not in PL/pgSQL -- you need to return each row with RETURN NEXT, generally from within a loop. Why do you want to avoid that? For simple functions you could use SQL instead of PL/pgSQL: http://www.postgresql.org/docs/8.1/interactive/xfunc-sql.html#AEN31627 > Also, the WHERE part is also important cause I'm not sure i got > that part right? Would this call for EXECUTE or will it be okay and > be planned the first time by the query planner? If each call to the function issues the same query, just with different values, then you shouldn't need to use EXECUTE. If the query differs depending on the function parameters then you have several possibilities: * You could build the query string and use EXECUTE. Be sure to read about quote_literal() and quote_ident(). * You could use an IF statement to execute the query you need. * You could put the queries in separate functions. You can use the same name for different functions if their call signatures are different, e.g., getrecord(integer) and getrecord(integer, text). * You could rewrite the query, possibly using CASE or COALESCE to handle NULL values. -- Michael Fuhr
Michael Fuhr <mike@fuhr.org> wrote:
On Thu, Jan 05, 2006 at 12:50:34AM -0800, Matthew Peter wrote:I was thinking it would be more efficient to pull all the records in one call rather than 50 calls. For all I know it probably executes 50 calls in the internals when translating the IN (IDs).
> Is it possible to skip the loop and just return all records in a
> single query and shove all those rows into a table variable?
Not in PL/pgSQL -- you need to return each row with RETURN NEXT,
generally from within a loop. Why do you want to avoid that?
* You could use an IF statement to execute the query you need.That's what I was trying to do, but I'm no t sure i was doing it in the right context, since it was IN the query, not testing after it. Figured I'd ask the list if I was trying something impossible or if I was close to help get me on track.
* You could put the queries in separate functions.The query is so similiar (occasionally match on extra WHERE arg) it would be nice just to use a conditional to match if that extra argument is given as not null...rather than maintain two simliar functions if possible, while keeping it planned after the first run.
Does using an IF predicate in the WHERE in the SQL call require EXECUTE since (I guess) I'm making the SQL statement somewhat dynamic? All I've been able to find is IF handling after the query, not in it.
Thanks again
Matt
Yahoo! DSL Something to write home about. Just $16.99/mo. or less
On Fri, Jan 06, 2006 at 01:14:38AM -0800, Matthew Peter wrote: > Michael Fuhr <mike@fuhr.org> wrote: > > On Thu, Jan 05, 2006 at 12:50:34AM -0800, Matthew Peter wrote: > > > Is it possible to skip the loop and just return all records in a > > > single query and shove all those rows into a table variable? > > > > Not in PL/pgSQL -- you need to return each row with RETURN NEXT, > > generally from within a loop. Why do you want to avoid that? > > I was thinking it would be more efficient to pull all the records in > one call rather than 50 calls. For all I know it probably executes 50 > calls in the internals when translating the IN (IDs). I wouldn't worry about that unless you can demonstrate that it's causing a performance problem. Even then you're stuck because that's how set-returning functions work. > > * You could use an IF statement to execute the query you need. > > That's what I was trying to do, but I'm not sure i was doing it in > the right context, since it was IN the query, not testing after it. > Figured I'd ask the list if I was trying something impossible or if > I was close to help get me on track. The IF statement needs to be part of the PL/pgSQL logic, not part of the query string. However, you might be able to use CASE or COALESCE in the query, as in WHERE my_tbl_id = $1 AND CASE WHEN $2 IS NULL THEN TRUE ELSE $2 = username END or WHERE my_tbl_id = $1 AND COALESCE($2 = username, TRUE) or WHERE my_tbl_id = $1 AND COALESCE($2, username) = username With predicates such as these you wouldn't need to use EXECUTE and you could write the query only once. -- Michael Fuhr
Michael Fuhr <mike@fuhr.org> wrote:
On Fri, Jan 06, 2006 at 01:14:38AM -0800, Matthew Peter wrote:I'll try that out tomorrow. Thanks Micheal
> Michael Fuhr wrote:
> > On Thu, Jan 05, 2006 at 12:50:34AM -0800, Matthew Peter wrote:
> > > Is it possible to skip the loop and just return all records in a
> > > single query and shove all those rows into a table variable?
> >
> > Not in PL/pgSQL -- you need to return each row with RETURN NEXT,
> > generally from within a loop. Why do you want to avoid that?
>
> I was thinking it would be more efficient to pull all the records in
> one call rather than 50 calls. For all I know it probably executes 50
> calls in the internals when translating the IN (IDs).
I wouldn't worry about that unless you can demonstrate that it's
causing a performance problem. Even then you're stuck because
that's how set-returning functions work.
> > * You could use an IF statement to execute the query you need.
>
> That's what I was trying to do, but I'm not sure i was doing it in
> the right context, since it was IN the query, not testing after it.
> Figured I'd ask the list if I was trying something impossible or if
> I was close to help get me on track.
The IF statement needs to be part of the PL/pgSQL logic, not part
of the query string. However, you might be able to use CASE or
COALESCE in the query, as in
WHERE my_tbl_id = $1
AND CASE WHEN $2 IS NULL THEN TRUE ELSE $2 = username END
or
WHERE my_tbl_id = $1 AND COALESCE($2 = username, TRUE)
or
WHERE my_tbl_id = $1 AND COALESCE($2, username) = username
With predicates such as these you wouldn't need to use EXECUTE and< br>you could write the query only once.
--
Michael Fuhr
Yahoo! DSL Something to write home about. Just $16.99/mo. or less
snip
One other quick question, (figure it still applies to the subject line :) when returning a row from a function I'm trying to include an aggregate, but it's not showing up in the query result and I think it's because it's not included in the RETURN NEXT row;? How do I return it as part of the resultset...
WHERE my_tbl_id = $1That did work. Thanks.
AND CASE WHEN $2 IS NULL THEN TRUE ELSE $2 = username END
or
WHERE my_tbl_id = $1 AND COALESCE($2 = username, TRUE)
or
WHERE my_tbl_id = $1 AND COALESCE($2, username) = username
With predicates such as these you wouldn't need to use EXECUTE and
you could write the query only once.
One other quick question, (figure it still applies to the subject line :) when returning a row from a function I'm trying to include an aggregate, but it's not showing up in the query result and I think it's because it's not included in the RETURN NEXT row;? How do I return it as part of the resultset...
create or replace function getrecord(int,text) RETURNS SETOF my_tblas $$Thanks
DECLARE
row my_tbl%rowtype;
BEGIN
FOR row IN SELECT *, SUBSTR(title,1,25) as short_title FROM my_tbl
WHERE ...
LOOP
RETURN NEXT row;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
Yahoo! Photos
Got holiday prints? See all the ways to get quality prints in your hands ASAP.
On Mon, Jan 09, 2006 at 01:01:33PM -0800, Matthew Peter wrote: > One other quick question, (figure it still applies to the subject > line :) when returning a row from a function I'm trying to include an > aggregate, but it's not showing up in the query result and I think > it's because it's not included in the RETURN NEXT row;? How do I > return it as part of the resultset... Terminology point: you used the word "aggregate" but the function below doesn't have an aggregate. Aggregates are functions that operate on multiple rows, like count() and sum(); substr() doesn't do that so it's not an aggregate. > create or replace function getrecord(int,text) RETURNS SETOF my_tbl as $$ > DECLARE > row my_tbl%rowtype; > > BEGIN > FOR row IN SELECT *, SUBSTR(title,1,25) as short_title FROM my_tbl [...] You've declared the row variable to be of type my_tbl so whatever columns my_tbl has are the columns you get. If you want to return additional columns then you have a few choices: 1. Create a composite type with the desired columns, declare the function to return SETOF that type, and declare row to be of that type. 2. Declare the function to return SETOF record, declare row to be of type record, and provide a column definition list when you call the function. 3. Use OUT parameters (new in 8.1). -- Michael Fuhr
Terminology point: you used the word "aggregate" but the functionya. my mistake.
below doesn't have an aggregate. Aggregates are functions that
operate on multiple rows, like count() and sum(); substr() doesn't
do that so it's not an aggregate.
[snip]
1. Create a composite type with the desired columns, declare thek. this is where i was confused. this is exactly what i wanted/needed
function to return SETOF that type, and declare row to be of
that type.
Thanks
Yahoo! Photos Showcase holiday pictures in hardcover
Photo Books. You design it and well bind it!