Thread: Function to either return one or all records

Function to either return one or all records

From
KÖPFERL Robert
Date:
Hi all,

I think I have got a usual problem. I'm asking here, however, because I
wonder why it works this way.

The problem is to write a fcn that eihter returns all records or just
one/none filtered by some expression. For example get a value by id or
return all values if the given id is null. 

For a table like this (id (PK) int | val int ) one would write a function
like this:

CREATE OR REPLACE FUNCTION getval(integer) RETURNS SETOF id_val_tbl AS
$BODY$
select * from id_bal_tbl where ( $1 is null )or (id=$1 );
$BODY$ LANGUAGE 'sql' VOLATILE SECURITY DEFINER;


It works fine, however an index is never used (if just one record is
requested). The column id has a btree-Index but what aobut it. I'm wondering
how this comes and how one can overcome this limit.
I read that btree and null values do not like each other very well. And
surely, if the Select is transformed reduced to 
select * from id_bal_tbl where (false)or (id=$1 );
the Index is used!
What I don't get is: When expanding parameters in the SQL-function it will
evaluate to the statement above. Bevore any data is looked up. Why is that? 




Thanks again in advance


Re: Function to either return one or all records

From
"Tambet Matiisen"
Date:
>
> CREATE OR REPLACE FUNCTION getval(integer)
>   RETURNS SETOF id_val_tbl AS
> $BODY$
> select * from id_bal_tbl where ( $1 is null )or (id=$1 ); $BODY$
>   LANGUAGE 'sql' VOLATILE SECURITY DEFINER;
>
>
> It works fine, however an index is never used (if just one
> record is requested). The column id has a btree-Index but
> what aobut it. I'm wondering how this comes and how one can
> overcome this limit.

The reason why the query worked as plain query may come from the fact that NULL IS NULL was evaluated to constant FALSE
andoptimized out from OR. In case of function the query was planned before substituting parameters, so the OR was still
thereand prevented index scan. 

Standard technique is to rewrite OR queries to UNION queries. I believe PostgreSQL optimizer does not do that
automatically.So you could try instead: 

select * from id_bal_tbl where $1 is null
union all
select * from id_bal_tbl where id = $1;

(Note: in general you would need UNION without ALL, to keep the semantics of OR.)
 Tambet


Re: Function to either return one or all records

From
Tom Lane
Date:
"Tambet Matiisen" <t.matiisen@aprote.ee> writes:
> Standard technique is to rewrite OR queries to UNION queries. I believe PostgreSQL optimizer does not do that
automatically.So you could try instead:
 

> select * from id_bal_tbl where $1 is null
> union all
> select * from id_bal_tbl where id = $1;

... but that won't do anything to solve the performance problem.
Doesn't help for the second select to use an index, if the first
one grovels over the whole table anyway ...
        regards, tom lane


Re: Function to either return one or all records

From
KÖPFERL Robert
Date:
Hmmm, you're right.
Is there any way to do a

explain select * from id_val_tbl where false

while explain doesn't tell SEQ_SCAN?

I have to admit that I didn't performance tested this. I just saw explain
writing SEQ_SCAN.

|-----Original Message-----
|From: Tom Lane [mailto:tgl@sss.pgh.pa.us]
|Sent: Mittwoch, 20. April 2005 23:06
|To: Tambet Matiisen
|Cc: KÖPFERL Robert; pgsql-sql@postgresql.org
|Subject: Re: [SQL] Function to either return one or all records
|
|
|"Tambet Matiisen" <t.matiisen@aprote.ee> writes:
|> Standard technique is to rewrite OR queries to UNION
|queries. I believe PostgreSQL optimizer does not do that
|automatically. So you could try instead:
|
|> select * from id_bal_tbl where $1 is null
|> union all
|> select * from id_bal_tbl where id = $1;
|
|... but that won't do anything to solve the performance problem.
|Doesn't help for the second select to use an index, if the first
|one grovels over the whole table anyway ...
|
|            regards, tom lane
|


Re: Function to either return one or all records

From
"Tambet Matiisen"
Date:

> -----Original Message-----
> From: Tom Lane [mailto:tgl@sss.pgh.pa.us]
> Sent: Thursday, April 21, 2005 12:06 AM
> To: Tambet Matiisen
> Cc: KÖPFERL Robert; pgsql-sql@postgresql.org
> Subject: Re: [SQL] Function to either return one or all records
>
>
> "Tambet Matiisen" <t.matiisen@aprote.ee> writes:
> > Standard technique is to rewrite OR queries to UNION queries. I
> > believe PostgreSQL optimizer does not do that automatically. So you
> > could try instead:
>
> > select * from id_bal_tbl where $1 is null
> > union all
> > select * from id_bal_tbl where id = $1;
>
> ... but that won't do anything to solve the performance
> problem. Doesn't help for the second select to use an index,
> if the first one grovels over the whole table anyway ...
>

Hmm, I tought about that before sending mail to list and did a little test. My test indicated, that the seq scan was
discarded,it was definitely faster than OR query. I couldn't verify plan, as EXPLAIN shows only function scan. 
 Tambet