I am trying to retrieve a random record (according to a chance
attribute) from a small set of records, each with a "chance" attribute.
This may eventually be somwhat of a performance concern, so I'd like to
make sure I'm doing this right.
Here's what I have so far:
create table r1 (
i int,
chance numeric
)
create or replace function randrec() returns int as $$
$res = spi_exec_query('select i,chance from r1');
$r = rand;
$accum = 0;
$i = 0;
while($accum < $r) {
$accum += $res->{rows}[$i++]->{chance}
}
return $res->{rows}[$i-1]->{i};
$$ language plperl;
test=# select * from r1;
i | chance
---+--------
1 | 0.25
2 | 0.20
3 | 0.15
4 | 0.10
5 | 0.30
That seems to work, in that out of 10k times, I got the following
numbers of each:
1 2479
2 1959
3 1522
4 950
5 3090
But I have a few questions:
* Am I right to use NUMERIC for the chance attribute?
* Does perl's arithmetic leave me with the chance that those numeric
values don't add up to 1.00 (and in this case that could mean an
infinite loop)?
* In my design I'll need a constraint trigger making sure that the
numbers add up to 1.00. Will that be a performance problem for
operations on the table that don't modify the chance attribute?
* Is there a better way?
* Does spi_exec_query pull the entire result set into memory at once? Is
there a point at which performance could be a serious problem if there
are a large number of items to select among?
Regards,
Jeff Davis