cr_bucket_dedicated_code.sql

This page documents the preview version (v2.21). Preview includes features under active development and is for development and testing only. For production, use the stable version (v2024.1). To learn more, see Versioning.

Save this script as cr_bucket_dedicated_code.sql.

-- This approach implements the required "open-closed" interval
-- bucket semantics directly. See the test
--
--   where scaled_val > lb and scaled_val <= ub
--
-- at the end.
--
-- You might consider this to be the better approach,  even though
-- it means saying "No thank you" to the free gift of the built-in
-- "width_bucket()" which -- acutally implements the wrong semantics
-- for the present use case. Fixing it up with a trick might feel to be
-- too offensive.
--
-- This implementation of "bucket()" also passes the rigorous
-- acceptance test.

create or replace function bucket(
  val          in double precision,
  lower_bound  in double precision default 0,
  upper_bound  in double precision default 1,
  no_of_values in int              default 10)
  returns int
  language plpgsql
as $body$
begin
  assert
    (val between lower_bound and upper_bound),
    'bucket():'||
    ' val '||val||
    ' must be between lower_bound '||lower_bound||
    ' and upper_bound '||upper_bound;

  declare
    one  constant double precision := 1;
    n    constant double precision := no_of_values;
    scaled_val constant double precision :=
      (val - lower_bound)/(upper_bound - lower_bound);
  begin
    return (
      with
        series as (
          select generate_series::int as s
          from generate_series(1::int, no_of_values))
        ,
        buckets as (
          select
            s                                             as bucket,
            -- (val = 0) is defined to be in (bucket = 1)
            case s when 1 then
              -one
            else
              ((s::double precision) - one)/n
            end                                           as lb,
            (s::double precision)/n                       as ub
          from series)

      select bucket
      from buckets
      where scaled_val > lb and scaled_val <= ub
      );
  end;
end;
$body$;