Thread: Logical Aggregate Functions (eg ANY())

Logical Aggregate Functions (eg ANY())

From
Robert James
Date:
I see Postgres (I'm using 8.3) has bitwise aggregate functions
(bit_or), but doesn't seem to have logical aggregate functions.

How do I do the equivalent of an ANY() or ALL() in PG Aggregate SQL?

Re: Logical Aggregate Functions (eg ANY())

From
Merlin Moncure
Date:
On Thu, Dec 15, 2011 at 10:10 AM, Robert James <srobertjames@gmail.com> wrote:
> I see Postgres (I'm using 8.3) has bitwise aggregate functions
> (bit_or), but doesn't seem to have logical aggregate functions.
>
> How do I do the equivalent of an ANY() or ALL() in PG Aggregate SQL?



CREATE OR REPLACE FUNCTION OrAgg(bool, bool) RETURNS BOOL AS
$$
  SELECT COALESCE($1 or $2, false);
$$ LANGUAGE SQL IMMUTABLE;

create aggregate "any"(bool)
(
  sfunc=OrAgg,
  stype=bool
);

postgres=# select "any"(v) from (values (false), (true)) q(v);
 any
-----
 t
(1 row)

etc

note:, I don't like the use of double quoted "any" -- but I'm too lazy
to come up with a better name.  :-)

merlin

Re: Logical Aggregate Functions (eg ANY())

From
Kirill Simonov
Date:
On 12/15/2011 11:10 AM, Robert James wrote:
> I see Postgres (I'm using 8.3) has bitwise aggregate functions
> (bit_or), but doesn't seem to have logical aggregate functions.
>

They are called BOOL_AND and BOOL_OR, see
http://www.postgresql.org/docs/8.3/interactive/functions-aggregate.html


Thanks,
Kirill

Re: Logical Aggregate Functions (eg ANY())

From
Marti Raudsepp
Date:
On Thu, Dec 15, 2011 at 18:10, Robert James <srobertjames@gmail.com> wrote:
> How do I do the equivalent of an ANY() or ALL() in PG Aggregate SQL?

Note that in many cases, writing an EXISTS(SELECT ...) or NOT
EXISTS(...) subquery is faster, since the planner can often optimize
those to a single index access -- whereas an aggregate function would
necessarily need to walk through and evaluate all potential rows.

Regards,
Marti

Re: Logical Aggregate Functions (eg ANY())

From
Robert James
Date:
On 12/15/11, Marti Raudsepp <marti@juffo.org> wrote:
> On Thu, Dec 15, 2011 at 18:10, Robert James <srobertjames@gmail.com> wrote:
>> How do I do the equivalent of an ANY() or ALL() in PG Aggregate SQL?
>
> Note that in many cases, writing an EXISTS(SELECT ...) or NOT
> EXISTS(...) subquery is faster, since the planner can often optimize
> those to a single index access -- whereas an aggregate function would
> necessarily need to walk through and evaluate all potential rows.
>

Really? The planner can't tell that, for instance, BOOL_AND (false, *)
is automatically false?

(BTW Thanks for the great responses everyone! On this and other posts
on this list)

Re: Logical Aggregate Functions (eg ANY())

From
Merlin Moncure
Date:
On Sat, Dec 17, 2011 at 6:06 PM, Robert James <srobertjames@gmail.com> wrote:
> On 12/15/11, Marti Raudsepp <marti@juffo.org> wrote:
>> On Thu, Dec 15, 2011 at 18:10, Robert James <srobertjames@gmail.com> wrote:
>>> How do I do the equivalent of an ANY() or ALL() in PG Aggregate SQL?
>>
>> Note that in many cases, writing an EXISTS(SELECT ...) or NOT
>> EXISTS(...) subquery is faster, since the planner can often optimize
>> those to a single index access -- whereas an aggregate function would
>> necessarily need to walk through and evaluate all potential rows.
>>
>
> Really? The planner can't tell that, for instance, BOOL_AND (false, *)
> is automatically false?

No (by the way, I really should have known about the bool_x aggregate
functions before suggesting a  hand rolled one!), that would require
that the planner have very special understanding of the internal
workings of aggregate functions.  There are a couple of cases where
the planner *does* have that function, for example it can convert
max(v) to 'order by v desc limit 1' to bag the index, but that's the
exception rather than the rule.

Most queries that can be equivalently expressed in aggregate and
non-aggregate form are faster without aggregates.   However,
aggregation can be a cleaner expression of the problem which is
important as well (performance isn't everything!).

merlin

Re: Logical Aggregate Functions (eg ANY())

From
Marti Raudsepp
Date:
On Mon, Dec 19, 2011 at 06:32, Merlin Moncure <mmoncure@gmail.com> wrote:
> that would require
> that the planner have very special understanding of the internal
> workings of aggregate functions.  There are a couple of cases where
> the planner *does* have that function, for example it can convert
> max(v) to 'order by v desc limit 1'

In fact, there's no reason why bool_or/bool_and couldn't do the same
thing. bool_or() is like the max() for boolean values, and bool_and()
is min().

CREATE AGGREGATE my_bool_or(bool) (sfunc=boolor_statefunc, stype=bool,
sortop= >);
CREATE AGGREGATE my_bool_and(bool) (sfunc=booland_statefunc,
stype=bool, sortop= <);

db=# explain analyze select bool_and(b) from bools;
 Aggregate  (cost=1693.01..1693.02 rows=1 width=1)
   ->  Seq Scan on bools  (cost=0.00..1443.01 rows=100001 width=1)
 Total runtime: 29.736 ms

db=# explain analyze select my_bool_and(b) from bools;
 Result  (cost=0.03..0.04 rows=1 width=0)
   InitPlan 1 (returns $0)
     ->  Limit  (cost=0.00..0.03 rows=1 width=1)
           ->  Index Scan using bools_b_idx on bools
(cost=0.00..3300.28 rows=100001 width=1)
                 Index Cond: (b IS NOT NULL)
 Total runtime: 0.109 ms

Now obviously this still has limitations -- it doesn't do index
accesses in a GROUP BY query -- but it's a fairly simple modification.

Regards,
Marti

Re: Logical Aggregate Functions (eg ANY())

From
Merlin Moncure
Date:
On Mon, Dec 19, 2011 at 3:42 AM, Marti Raudsepp <marti@juffo.org> wrote:
> In fact, there's no reason why bool_or/bool_and couldn't do the same
> thing. bool_or() is like the max() for boolean values, and bool_and()
> is min().
>
> CREATE AGGREGATE my_bool_or(bool) (sfunc=boolor_statefunc, stype=bool,
> sortop= >);
> CREATE AGGREGATE my_bool_and(bool) (sfunc=booland_statefunc,
> stype=bool, sortop= <);
>
> db=# explain analyze select bool_and(b) from bools;
>  Aggregate  (cost=1693.01..1693.02 rows=1 width=1)
>   ->  Seq Scan on bools  (cost=0.00..1443.01 rows=100001 width=1)
>  Total runtime: 29.736 ms
>
> db=# explain analyze select my_bool_and(b) from bools;
>  Result  (cost=0.03..0.04 rows=1 width=0)
>   InitPlan 1 (returns $0)
>     ->  Limit  (cost=0.00..0.03 rows=1 width=1)
>           ->  Index Scan using bools_b_idx on bools
> (cost=0.00..3300.28 rows=100001 width=1)
>                 Index Cond: (b IS NOT NULL)
>  Total runtime: 0.109 ms
>
> Now obviously this still has limitations -- it doesn't do index
> accesses in a GROUP BY query -- but it's a fairly simple modification.

That's really clever...bravo.

merlin