Обсуждение: Returning multiple Rows from PL/pgSQL-Function

Поиск
Список
Период
Сортировка

Returning multiple Rows from PL/pgSQL-Function

От
Alvar Freude
Дата:
Hi,

I want to create a function (PL/pgSQL), which return multiple rows. But it
fails -- when Creating the function, I get a notice:
 NOTICE:  ProcedureCreate: return type 'records' is only a shell


When executing it, this error:
 ERROR:  fmgr_info: function 0: cache lookup failed


How should I do this?


The function is simple:
  CREATE FUNCTION foo (timestamp) RETURNS SETOF records AS        '       BEGIN          RETURN SELECT * FROM table;
  END;       ' LANGUAGE 'plpgsql';  
 


or, in more detail the exact function:

  CREATE FUNCTION get_emotions (timestamp) RETURNS SETOF records AS         '        DECLARE            start ALIAS FOR
$1;          end_id int4;        BEGIN           SELECT emotion_id FROM emotions                             WHERE date
<=start                             LIMIT 1                              INTO end_id;           RETURN SELECT
emotion_id,emotion1, [...]                 FROM  emotions                  WHERE emotion_id BETWEEN end_id-3000 AND
end_id                ORDER BY date_epoch + full_rating*(3600*12)                 LIMIT 300;        END;        '
LANGUAGE'plpgsql'; 
 



Thanx for any help!


Ciao Alvar



-- 
| AGI ............................................................... |
| Magirusstrasse 21B, 70469 Stuttgart . Fon +49 (0)711.228 74-50 .... |
| http://www.agi.com/diary/ (english) . http://www.agi.de/tagebuch/ . |
| >>>>>> NEWS >>> AGI holt Bronze-Loewen in Cannes! <<<<<<<<<<<<<<<<< |




Re: Returning multiple Rows from PL/pgSQL-Function

От
"Richard Huxton"
Дата:
From: "Alvar Freude" <alvar@agi.de>

> Hi,
>
> I want to create a function (PL/pgSQL), which return multiple rows. But it
> fails -- when Creating the function, I get a notice:

> How should I do this?

Can't at the moment.

> or, in more detail the exact function:
>
>
>    CREATE FUNCTION get_emotions (timestamp) RETURNS SETOF records AS
>          '
>          DECLARE
>             start ALIAS FOR $1;
>             end_id int4;
>          BEGIN
>             SELECT emotion_id FROM emotions
>                              WHERE date <= start
>                              LIMIT 1
>                               INTO end_id;

Not entirely clear what your function is for, but the above select looks a
bit odd. Do you not want to "order by" here so you can get the "most recent"
emotion_id or whatever?

>             RETURN SELECT emotion_id, emotion1, [...]
>                   FROM  emotions
>                   WHERE emotion_id BETWEEN end_id-3000 AND end_id
>                   ORDER BY date_epoch + full_rating*(3600*12)
>                   LIMIT 300;
>          END;
>          '
>       LANGUAGE 'plpgsql';

I'd rewrite this as just a select, or a view if you want to keep things
clean in the application, possibly with that first select encapsulated in a
function (sorry, I'm not entirely clear what your code is doing).

so:

CREATE VIEW get_emotions_view AS
SELECT emotion_id, emotion1, ...
ORDER BY date_epoch + full_rating*3600*12
LIMIT 300;

and then issue a query like:

SELECT * FROM get_emotions view
WHERE emotion_id
BETWEEN last_em_id(<start val here>)-3000 AND last_em_id(<start val here>);

If you set the "is_cachable" flag on the last_em_id() function it should
only be calculated once.

HTH

- Richard Huxton



Re: Returning multiple Rows from PL/pgSQL-Function

От
Alex Pilosov
Дата:
Currently, this is not possible.

It will be possible in 7.2, or with a patch I'm working on...

On Mon, 9 Jul 2001, Alvar Freude wrote:

> Hi,
> 
> I want to create a function (PL/pgSQL), which return multiple rows. But it
> fails -- when Creating the function, I get a notice:
> 
>   NOTICE:  ProcedureCreate: return type 'records' is only a shell
> 
> 
> When executing it, this error:
> 
>   ERROR:  fmgr_info: function 0: cache lookup failed
> 
> 
> How should I do this?
> 
> 
> The function is simple:
> 
>    CREATE FUNCTION foo (timestamp) RETURNS SETOF records AS 
>         '
>         BEGIN
>            RETURN SELECT * FROM table;
>         END;
>         ' LANGUAGE 'plpgsql';
>    
> 
> 
> or, in more detail the exact function:
> 
> 
>    CREATE FUNCTION get_emotions (timestamp) RETURNS SETOF records AS 
>          '
>          DECLARE 
>             start ALIAS FOR $1;
>             end_id int4;
>          BEGIN
>             SELECT emotion_id FROM emotions 
>                              WHERE date <= start 
>                              LIMIT 1 
>                               INTO end_id;
>             RETURN SELECT emotion_id, emotion1, [...]
>                   FROM  emotions 
>                   WHERE emotion_id BETWEEN end_id-3000 AND end_id
>                   ORDER BY date_epoch + full_rating*(3600*12)
>                   LIMIT 300;
>          END;
>          '
>       LANGUAGE 'plpgsql'; 
> 
> 
> 
> Thanx for any help!
> 
> 
> Ciao
>   Alvar
> 
> 
> 
> 




Re: Returning multiple Rows from PL/pgSQL-Function

От
Alvar Freude
Дата:
>> How should I do this?
> 
> Can't at the moment.

ups, OK -- then I misunderstand something ;)

>> or, in more detail the exact function:
>> 
>> 
>>    CREATE FUNCTION get_emotions (timestamp) RETURNS SETOF records AS
>>          '
>>          DECLARE
>>             start ALIAS FOR $1;
>>             end_id int4;
>>          BEGIN
>>             SELECT emotion_id FROM emotions
>>                              WHERE date <= start
>>                              LIMIT 1
>>                               INTO end_id;
> 

> Not entirely clear what your function is for, but the above select looks a
> bit odd. Do you not want to "order by" here so you can get the "most
> recent" emotion_id or whatever?

In detail, I want 300 rows older then a specific date (timeslider), but
they are sorted by time AND an additional rating. For this i have to sort
the hole table without index -- but if i presort the 3000 rows before the
specific date and catch the 300 best rated/timed rows, i save lot of time. 

It's not critical if there are some faulty rows selected ...

This is the only reason to select a subpart (3000 Rows) of the table bevore
doing the final selection which rows should be taken.


> I'd rewrite this as just a select, or a view if you want to keep things
> clean in the application, possibly with that first select encapsulated in
> a function (sorry, I'm not entirely clear what your code is doing).
>
> so:
> 
> CREATE VIEW get_emotions_view AS
> SELECT emotion_id, emotion1, ...
> ORDER BY date_epoch + full_rating*3600*12
> LIMIT 300;

hmmm, but with this, the hole ORDER BY goes throug the hole table (might be
a lot of rows), with not using the index.

For now i do the hole stuff on client side with two selects:
First selecting the end_id, then (2. Statement) sort the stuff within
end_id and end_id-3000 and return the 300 most "best".


my $end_id = $self->db_h->selectrow_array(        "SELECT emotion_id            FROM emotions           WHERE date <= ?
         ORDER BY date DESC          LIMIT 1",          undef,           $self->date_from_sliderpos($params[0]));
 

my $st_h = $self->db_h->prepare(        "        SELECT emotion_id, emotion1, ..., full_rating, date
FROMemotions                     WHERE emotion_id BETWEEN ? AND ?                    ORDER BY date_epoch +
full_rating*(3600*12)                   LIMIT 300        ");
 

$st_h->execute($end_id-3000, $end_id) or die "execute kaputt";

$st_h->bind_columns(...); 
[...]


Thanks and Ciao
 Alvar


-- 
| AGI ............................................................... |
| Magirusstrasse 21B, 70469 Stuttgart . Fon +49 (0)711.228 74-50 .... |
| http://www.agi.com/diary/ (english) . http://www.agi.de/tagebuch/ . |
| >>>>>> NEWS >>> AGI holt Bronze-Loewen in Cannes! <<<<<<<<<<<<<<<<< |




Re: Returning multiple Rows from PL/pgSQL-Function

От
"Richard Huxton"
Дата:
From: "Alvar Freude" <alvar@agi.de>

> For now i do the hole stuff on client side with two selects:
> First selecting the end_id, then (2. Statement) sort the stuff within
> end_id and end_id-3000 and return the 300 most "best".
>
>
> my $end_id = $self->db_h->selectrow_array(
>          "SELECT emotion_id
>             FROM emotions
>            WHERE date <= ?
>            ORDER BY date DESC
>            LIMIT 1",
>            undef,
>            $self->date_from_sliderpos($params[0]));
>
> my $st_h = $self->db_h->prepare(
>          "
>          SELECT emotion_id, emotion1, ..., full_rating, date
>                      FROM emotions
>                      WHERE emotion_id BETWEEN ? AND ?
>                      ORDER BY date_epoch + full_rating*(3600*12)
>                      LIMIT 300
>          ");
>
> $st_h->execute($end_id-3000, $end_id) or die "execute kaputt";

So - basically you want something like:

SELECT * from emotions
WHERE emotion_date <= [cutoff time]
ORDER BY calculated_score(date_epoch,full_rating)
LIMIT 300

Where you'd have an index on "calculated_score". Well - you can either have
a "score" field and use triggers to keep it up to date or build an index on
the "calculated_score()" function. Depends on your pattern of usage which is
going to be better for you.

You can create a functional index as easily as a normal one:

CREATE FUNCTION calculated_score(integer, integer) RETURNS integer AS '
BEGIN RETURN $1 + ($2 + 3600 + 12)
END;
' LANGUAGE 'plpgsql';

CREATE INDEX emot_calc_idx
ON emotions ( calculated_score(date_epoch, full_rating) );



If you've never used triggers before, there is a section in the docs and
also some examples at techdocs.postgresql.org

Is that the sort of thing you were after?

- Richard Huxton



Re: Returning multiple Rows from PL/pgSQL-Function

От
Alvar Freude
Дата:
Hi,

> So - basically you want something like:
> 
> SELECT * from emotions
> WHERE emotion_date <= [cutoff time]
> ORDER BY calculated_score(date_epoch,full_rating)
> LIMIT 300

yes, thats it -- nearly :-)

In detail the calculated_score is:

(cutoff_time - creation_time) + (sum_of_rating_points * rating_factor)


> Where you'd have an index on "calculated_score". Well - you can either
> have a "score" field and use triggers to keep it up to date 

yes, this was also my first idea, but this depends also on the [cutoff
time], so it can't work, because the trigger can't update this.



> or build an
> index on the "calculated_score()" function. Depends on your pattern of
> usage which is going to be better for you.
> 
> You can create a functional index as easily as a normal one:

aaah, wow, that's cool, I didn't know this before.


but here is the same: also the resulting order can't precalculated, if i'm
not completely wrong.


> Is that the sort of thing you were after?

nearly ;-)

In detail, I have the following:

Users enter from time to time some values (their "emotions") and place some
dots with this. Each dot has somethinglike a lifetime relative to the other
dots; this lifetime depends on the creation date and some rating of other
users.
Additionally it is possible to go back in the timeline and visit the stuff
from an earlyer view, but with new Voting. Always 300 dots are shown, but
not only the newest ones, there is also a chance to be viewed longer with
besser voting. 


It works now -- with the two statements I posted in the last message. It's
not exact because of the first select of 3000 dots -- if because of good
voting the 3001th dot should be visible it isn't, but thats not critical. 


Ciao Alvar


-- 
| AGI ............................................................... |
| Magirusstrasse 21B, 70469 Stuttgart . Fon +49 (0)711.228 74-50 .... |
| http://www.agi.com/diary/ (english) . http://www.agi.de/tagebuch/ . |
| >>>>>> NEWS >>> AGI holt Bronze-Loewen in Cannes! <<<<<<<<<<<<<<<<< |