Thread: ORDER BY with plpgsql parameter

ORDER BY with plpgsql parameter

From
Thomas Schoen
Date:
Hi,

is it possible to use a parameter of a plpgsql-function to order a selection
inside the function?
What i would like to do is pass a column-name/alias-name to a plpgsql function
and use the variable as order-by-parameter like this:

create function foo(varchar)
RETURNS SETOF test
AS '
    declare
        rec    test%ROWTYPE;
    begin
        FOR rec IN
            SELECT *
            FROM test
            ORDER BY $1
        LOOP
            RETURN NEXT rec;
        END LOOP;
        RETURN;
    end;
' LANGUAGE 'plpgsql';

This is only the core of what my function should do but can hopefully describe
what my question is about.
This function worked without error but did not sort anything.

I tried several solutions inside this function, e.g.:

SELECT *
FROM test
ORDER BY
    CASE
        WHEN $1 = ''foo'' THEN foo
        WHEN $1 = ''bar'' THEN bar
    END

wich throws the exception
"ERROR:  CASE types character varying and integer cannot be matched"
where "foo" is of type varchar and "bar" is of type int4.

Since i read in the docu, that "ORDER BY" accepts the numer of the column to
sort by, i tried:

SELECT *
FROM test
ORDER BY
    CASE
        WHEN $1 = ''foo'' THEN 1
        WHEN $1 = ''bar'' THEN 2
    END

This worked without exception but did not sort either.

I tried another one:

SELECT
        foo AS col1
        bar AS col2
FROM test
ORDER BY
    CASE
        WHEN $1 = ''foo'' THEN col1
        WHEN $1 = ''bar'' THEN col2
    END

This throws
"ERROR:  column "col1" does not exist.
Normal SQL-statements accept column-aliases as ORDER BY - criterium but inside
a CASE it does not seem to work.

It seems that with version 7.4 it is not possible to do what i tried.
Maybe some of the hackers want to put that on their list of feature requests
if they consider it a usefull feature.
It would be great to simply write it like this:
.... ORDER BY $1

I know that i can put the "ORDER BY" clause outside within the function call
(SELECT * FROM test('foo') ORDER BY foo) but since i want to use it together
with LIMIT AND OFFSET i don't want the function to return all rows and sort
it afterwards, because the count of rows returned would decrease the
functions performance enormously.

If someone knows a better solution i would be very gratefull.
Maybe a really have to build the statement as text and use it in a "FOR rec IN
EXECUTE". I hoped to find a usefull workaround for that hack.

best regards, tom schön

Re: ORDER BY with plpgsql parameter

From
"Lee Harr"
Date:
>is it possible to use a parameter of a plpgsql-function to order a
>selection
>inside the function?


You need to use the FOR-IN-EXECUTE style of query. That way
you can use any string you want (including text passed in as
a parameter) to build the query inside the function ...


http://www.postgresql.org/docs/7.4/static/plpgsql-control-structures.html
( it is the very last thing on that page )

_________________________________________________________________
Add photos to your e-mail with MSN 8. Get 2 months FREE*.
http://join.msn.com/?page=features/featuredemail


Re: ORDER BY with plpgsql parameter

From
Thomas Schoen
Date:
> You need to use the FOR-IN-EXECUTE style of query. That way
> you can use any string you want (including text passed in as
> a parameter) to build the query inside the function ...

that is what i want to avoid. (i wrote that in my first mail)
My question was about why it is not possible to do it like this:
....ORDER BY $1
I don't know if it is standards-conform or if someone considers it a usefull
feature, but i think it would help users to write capsulated
statistics-functions or simply browsable and orderable lists.

Re: ORDER BY with plpgsql parameter

From
Richard Huxton
Date:
Thomas Schoen wrote:
>>You need to use the FOR-IN-EXECUTE style of query. That way
>>you can use any string you want (including text passed in as
>>a parameter) to build the query inside the function ...
>
>
> that is what i want to avoid. (i wrote that in my first mail)
> My question was about why it is not possible to do it like this:
> ....ORDER BY $1
> I don't know if it is standards-conform or if someone considers it a usefull
> feature, but i think it would help users to write capsulated
> statistics-functions or simply browsable and orderable lists.

You want to build a dynamic query (sorted in different ways depending on
a function parameter).

You don't want to use the dynamic query statement (EXECUTE).

The whole point of plpgsql is that the queries can be compiled and
pre-planned. If you want to change the sorting then that implies a
different plan, which implies using the dynamic query feature.

What you want to do is possible if you use one of the interpreted
languages, e.g. pltcl/plperl (plphp?). Of course, that means none of
your query plans get compiled.

--
   Richard Huxton
   Archonet Ltd

Re: ORDER BY with plpgsql parameter

From
Clodoaldo Pinto Neto
Date:
I don't know if it can help but this is how I do it with SQL Server 2000:

CREATE PROCEDURE dbo.cbi_carregaDiretorios @equipe varchar( 20 ), @ordem char(
4 ) = 'nome' AS

select cad.cd_cnpjInt as cnpj, cad.nm_nome as nome
from dbSup001.dbo.sup_cad_instituicao as cad
    inner join dbSup001.dbo.sup_cod_equipe as equipe on cad.cd_codEquipeDir =
equipe.cd_equipe
    inner join dbCBI001.dbo.cbi_instituicoes as listaInst on listaInst.cnpj =
cad.cd_cnpjInt
where rtrim( equipe.ds_unidade + equipe.ds_subunidade + equipe.ds_equipe ) like
'%' + rtrim( @equipe ) + '%'
order by
    case when @ordem = 'nome' then cad.nm_nome end,
    case when @ordem = 'cnpj' then cnpj end
GO

The trick with SQL Server is that you have to use one case to each possible
column to be ordered.

Hope this helps.
Regards,
Clodoaldo Pinto

 --- Thomas Schoen <t.schoen@vitrado.de> escreveu: > Hi,
>
> is it possible to use a parameter of a plpgsql-function to order a selection
> inside the function?
> What i would like to do is pass a column-name/alias-name to a plpgsql
> function
> and use the variable as order-by-parameter like this:
>
> create function foo(varchar)
> RETURNS SETOF test
> AS '
>     declare
>         rec    test%ROWTYPE;
>     begin
>         FOR rec IN
>             SELECT *
>             FROM test
>             ORDER BY $1
>         LOOP
>             RETURN NEXT rec;
>         END LOOP;
>         RETURN;
>     end;
> ' LANGUAGE 'plpgsql';
>
> This is only the core of what my function should do but can hopefully
> describe
> what my question is about.
> This function worked without error but did not sort anything.
>
> I tried several solutions inside this function, e.g.:
>
> SELECT *
> FROM test
> ORDER BY
>     CASE
>         WHEN $1 = ''foo'' THEN foo
>         WHEN $1 = ''bar'' THEN bar
>     END
>
> wich throws the exception
> "ERROR:  CASE types character varying and integer cannot be matched"
> where "foo" is of type varchar and "bar" is of type int4.
>
> Since i read in the docu, that "ORDER BY" accepts the numer of the column to
> sort by, i tried:
>
> SELECT *
> FROM test
> ORDER BY
>     CASE
>         WHEN $1 = ''foo'' THEN 1
>         WHEN $1 = ''bar'' THEN 2
>     END
>
> This worked without exception but did not sort either.
>
> I tried another one:
>
> SELECT
>         foo AS col1
>         bar AS col2
> FROM test
> ORDER BY
>     CASE
>         WHEN $1 = ''foo'' THEN col1
>         WHEN $1 = ''bar'' THEN col2
>     END
>
> This throws
> "ERROR:  column "col1" does not exist.
> Normal SQL-statements accept column-aliases as ORDER BY - criterium but
> inside
> a CASE it does not seem to work.
>
> It seems that with version 7.4 it is not possible to do what i tried.
> Maybe some of the hackers want to put that on their list of feature requests
> if they consider it a usefull feature.
> It would be great to simply write it like this:
> .... ORDER BY $1
>
> I know that i can put the "ORDER BY" clause outside within the function call
> (SELECT * FROM test('foo') ORDER BY foo) but since i want to use it together
> with LIMIT AND OFFSET i don't want the function to return all rows and sort
> it afterwards, because the count of rows returned would decrease the
> functions performance enormously.
>
> If someone knows a better solution i would be very gratefull.
> Maybe a really have to build the statement as text and use it in a "FOR rec
> IN
> EXECUTE". I hoped to find a usefull workaround for that hack.
>
> best regards, tom schön
>
> ---------------------------(end of broadcast)---------------------------
> TIP 2: you can get off all lists at once with the unregister command
>     (send "unregister YourEmailAddressHere" to majordomo@postgresql.org)

______________________________________________________________________

Participe da pesquisa global sobre o Yahoo! Mail:
http://br.surveys.yahoo.com/global_mail_survey_br

Re: ORDER BY with plpgsql parameter

From
Stephan Szabo
Date:
On Wed, 2 Jun 2004, Thomas Schoen wrote:

> > You need to use the FOR-IN-EXECUTE style of query. That way
> > you can use any string you want (including text passed in as
> > a parameter) to build the query inside the function ...
>
> that is what i want to avoid. (i wrote that in my first mail)
> My question was about why it is not possible to do it like this:
> ....ORDER BY $1

One problem is that doing the above as column name would make the $1 have
a different meaning from its use in other places like where clauses (where
it acts like a bound parameter).

Apart from the quoting issue, I'm also not sure how it would be any
different from for-in-execute in any case.


Re: ORDER BY with plpgsql parameter

From
Thomas Schoen
Date:
> On Wed, 2 Jun 2004, Thomas Schoen wrote:
> > > You need to use the FOR-IN-EXECUTE style of query. That way
> > > you can use any string you want (including text passed in as
> > > a parameter) to build the query inside the function ...
> >
> > that is what i want to avoid. (i wrote that in my first mail)
> > My question was about why it is not possible to do it like this:
> > ....ORDER BY $1
>
> One problem is that doing the above as column name would make the $1 have
> a different meaning from its use in other places like where clauses (where
> it acts like a bound parameter).

I know what you mean.
I'm aware of this problem. I tired to quote the parameter using quote_ident
functions which did not work either.
I do not unserstand the logic behind parameters used in plpgsql-functions.
I don't know how they are bound inside the database-logic.

>
> Apart from the quoting issue, I'm also not sure how it would be any
> different from for-in-execute in any case.
Maybe thats just my preference.
I don't like the "build a string to interpret" kind of code.
But it would be interesting to know if their are any performance disadvantages
when using the "for in execute".
My experience of using "for in execute" was so far that it is much slower than
doing it the direct way - and way slower than using sql-functions instead of
plpgsql-functions.
Maybe that experience was subjective.


Re: ORDER BY with plpgsql parameter

From
Thomas Schoen
Date:
> You want to build a dynamic query (sorted in different ways depending on
> a function parameter).
yes.

>
> You don't want to use the dynamic query statement (EXECUTE).
yes, because it seems to me, that "for in execute" is slower than the direct
way. Is that right?
>
> The whole point of plpgsql is that the queries can be compiled and
> pre-planned. If you want to change the sorting then that implies a
> different plan, which implies using the dynamic query feature.
OK, does that mean, that it is no difference in performance whether i use "FOR
IN EXECUTE" or two different functions with different sorting?

>
> What you want to do is possible if you use one of the interpreted
> languages, e.g. pltcl/plperl (plphp?). Of course, that means none of
> your query plans get compiled.
What happens to the query plan if i use function-parameters in the where
clause of my statement? Is the function recompiled then?

Conclusion: if i want to sort inside the functions depending on function
parameters, the best way to do it is using "FOR IN EXECUTE"??
Is that right?

Re: ORDER BY with plpgsql parameter

From
DeJuan Jackson
Date:
The best of both world (speed and the ability to have different sort
options) that I can think of would be to use IF test with different
queries for the parameter.  That way you get pre-planning and can
specify the sort as a parameter.

Thomas Schoen wrote:

>>You want to build a dynamic query (sorted in different ways depending on
>>a function parameter).
>>
>>
>yes.
>
>
>
>>You don't want to use the dynamic query statement (EXECUTE).
>>
>>
>yes, because it seems to me, that "for in execute" is slower than the direct
>way. Is that right?
>
>
>>The whole point of plpgsql is that the queries can be compiled and
>>pre-planned. If you want to change the sorting then that implies a
>>different plan, which implies using the dynamic query feature.
>>
>>
>OK, does that mean, that it is no difference in performance whether i use "FOR
>IN EXECUTE" or two different functions with different sorting?
>
>
>
>>What you want to do is possible if you use one of the interpreted
>>languages, e.g. pltcl/plperl (plphp?). Of course, that means none of
>>your query plans get compiled.
>>
>>
>What happens to the query plan if i use function-parameters in the where
>clause of my statement? Is the function recompiled then?
>
>Conclusion: if i want to sort inside the functions depending on function
>parameters, the best way to do it is using "FOR IN EXECUTE"??
>Is that right?
>
>---------------------------(end of broadcast)---------------------------
>TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org
>
>


Re: ORDER BY with plpgsql parameter

From
Stephan Szabo
Date:
On Wed, 2 Jun 2004, Thomas Schoen wrote:

> > On Wed, 2 Jun 2004, Thomas Schoen wrote:
> > > > You need to use the FOR-IN-EXECUTE style of query. That way
> > > > you can use any string you want (including text passed in as
> > > > a parameter) to build the query inside the function ...
> > >
> > > that is what i want to avoid. (i wrote that in my first mail)
> > > My question was about why it is not possible to do it like this:
> > > ....ORDER BY $1
> >
> > One problem is that doing the above as column name would make the $1 have
> > a different meaning from its use in other places like where clauses (where
> > it acts like a bound parameter).
>
> I know what you mean.
> I'm aware of this problem. I tired to quote the parameter using quote_ident
> functions which did not work either.
> I do not unserstand the logic behind parameters used in plpgsql-functions.
> I don't know how they are bound inside the database-logic.

AFAIK it's similar to PREPARE/EXECUTE.  You have a number of arguments
whose values are substituted in as literals in those positions.

> > Apart from the quoting issue, I'm also not sure how it would be any
> > different from for-in-execute in any case.
> Maybe thats just my preference.
> I don't like the "build a string to interpret" kind of code.
> But it would be interesting to know if their are any performance disadvantages
> when using the "for in execute".

It does replan the query.  But you would need to presumably replan if you
were changing the order by column anyway so I'm not sure that allowing
order by $1 to mean order by an expression stored as text in $1 would
realistically have any different performance characteristics than the
execute case.

> My experience of using "for in execute" was so far that it is much slower than
> doing it the direct way - and way slower than using sql-functions instead of
> plpgsql-functions.
> Maybe that experience was subjective.

I've seen it both ways, it depends on alot of factors.

Re: ORDER BY with plpgsql parameter

From
Richard Huxton
Date:
Thomas Schoen wrote:
>>You want to build a dynamic query (sorted in different ways depending on
>>a function parameter).
>
> yes.
>
>>You don't want to use the dynamic query statement (EXECUTE).
>
> yes, because it seems to me, that "for in execute" is slower than the direct
> way. Is that right?

Sometimes yes, sometimes no.

>>The whole point of plpgsql is that the queries can be compiled and
>>pre-planned. If you want to change the sorting then that implies a
>>different plan, which implies using the dynamic query feature.
>
> OK, does that mean, that it is no difference in performance whether i use "FOR
> IN EXECUTE" or two different functions with different sorting?

I would expect two functions, each with a simple static query to be
faster. Of course, if you want to sort by a dozen different columns
separate functions may be too much trouble.

>>What you want to do is possible if you use one of the interpreted
>>languages, e.g. pltcl/plperl (plphp?). Of course, that means none of
>>your query plans get compiled.
>
> What happens to the query plan if i use function-parameters in the where
> clause of my statement? Is the function recompiled then?

If I understand you correctly, no. If you have a query like:
   SELECT * FROM table1 WHERE c < $1

At planning time, it doesn't know what value $1 will have, so it doesn't
know whether there will be few or many results returned. So, it makes a
best guess and that plan will be used every time you call the function.

The advantage to this is that for a complicated query with no or few
parameters, you don't have to re-plan the query every time you call the
function.

> Conclusion: if i want to sort inside the functions depending on function
> parameters, the best way to do it is using "FOR IN EXECUTE"??
> Is that right?

For plpgsql, yes.

--
   Richard Huxton
   Archonet Ltd

Re: ORDER BY with plpgsql parameter

From
Thomas Schoen
Date:
Thank you all for your detailed answers!

My conclusion is:
- Use "FOR IN EXECUTE" if you want to sort by many different columns depending
on the function paramaters.
- Use "IF" and a seperate SQL-statement for every sort-column provided by the
function parameter if you only want to sort by a few different columns and
want to optimize the performance of your function.