From: | mstory(at)uchicago(dot)edu |
---|---|
To: | "Jim C(dot) Nasby" <decibel(at)decibel(dot)org> |
Cc: | Greg Stark <gsstark(at)mit(dot)edu>, pgsql-general(at)postgresql(dot)org |
Subject: | Re: Calculating a moving average |
Date: | 2005-01-21 19:23:07 |
Message-ID: | 1106335387.41f1569b753eb@churlish.uchicago.edu |
Views: | Raw Message | Whole Thread | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-general |
Unless I'm grossly misunderstanding the problem i think that a trigger written
in PL/pgsql would work fine. Something like this:
CREATE TABLE foo (
foo_id SERIAL primary key,
foo TEXT);
CREATE TABLE bar (
foo_id INTEGER references foo,
bar_id SERIAL primary key,
bar DOUBLE PRECISION NOT NULL);
CREATE TABLE bar_avg (
foo_id INTEGER primary key references foo,
bar_avg DOUBLE PRECISION);
CREATE OR REPLACE FUNCTION get_bar_avg() RETURNS TRIGGER AS '
DECLARE
bar_record RECORD;
x INTEGER;
y DOUBLE PRECISION := 0;
BEGIN
IF TG_OP = ''INSERT'' THEN
y := y + NEW.bar;
x := 1;
FOR bar_record IN SELECT * FROM bar LOOP
IF NEW.foo_id = bar_record.foo_id THEN
y := y + bar_record.bar;
x := x + 1;
END IF;
END LOOP;
y := y/x;
IF EXISTS(SELECT * FROM bar_avg WHERE foo_id = NEW.foo_id) THEN
UPDATE bar_avg
SET bar_avg.bar_avg = y
WHERE foo_id = NEW.foo_id;
ELSE
INSERT INTO bar_avg VALUES (NEW.foo_id, y);
END IF;
RETURN NEW;
ELSIF TG_OP = ''DELETE'' THEN
x := 0;
FOR bar_record IN SELECT * FROM bar LOOP
IF OLD.foo_id = bar_record.foo_id AND OLD.bar_id <>
bar_record.bar_id THEN
y := y + bar_record.bar;
x := x + 1;
END IF;
END LOOP;
IF x <> 0 THEN
y := y/x;
END IF;
UPDATE bar_avg
SET bar_avg.bar_avg = y
WHERE foo_id = OLD.foo_id;
RETURN OLD;
ELSE
y := y + NEW.bar;
x := 1;
FOR bar_record IN SELECT * FROM bar LOOP
IF OLD.bar_id <> bar_record.bar_id THEN
y := y + bar_record.bar;
x := x + 1;
END IF;
END LOOP;
y := y/x;
UPDATE bar_avg
SET bar_avg.bar_avg = y
WHERE foo_id = OLD.foo_id;
RETURN NEW;
END IF;
END;
' LANGUAGE plpgsql;
CREATE TRIGGER get_bar_avg BEFORE INSERT OR DELETE OR UPDATE ON bar FOR EACH ROW
EXECUTE PROCEDURE get_bar_avg();
That should work,
regards,
matt
Quoting "Jim C. Nasby" <decibel(at)decibel(dot)org>:
> On Fri, Jan 21, 2005 at 12:53:45AM -0500, Greg Stark wrote:
> > "Vanole, Mike" <Mike(dot)Vanole(at)cingular(dot)com> writes:
> >
> > > I need to calculate a moving average and I would like to do it with
> SQL,
> > > or a Pg function built for this purpose. I'm on Pg 7.4. Is this
> possible
> > > in Pg without a bunch of self joins, or is there a funtion available?
> >
> > Unfortunately moving averages fall into a class of functions, called
> analytic
> > functions (at least that's what Oracle calls them) that are inherently hard
> to
> > model efficiently in SQL. Postgres doesn't have any special support for
> this
> > set of functions, so you're stuck doing it the inefficient ways that
> standard
> > SQL allows.
> >
> > I think this is even hard to implement correctly using Postgres's
> extremely
> > extensible function support. Even if you implemented it in Perl or Python
> I
> > don't think there's any way to allocate a temporary static storage area for
> a
> > given call site. So your moving average function would behave strangely if
> you
> > called it twice in a given query.
> >
> > But if you can work within that caveat it should be straightforward to
> > implement it efficiently in Perl or Python. Alternatively you can write a
> > plpgsql function to calculate the specific moving average you need that
> does
> > the select itself.
>
> If you're feeling adventurous, you might look at Oracle's documentation
> on their analytic functions and see if you can come up with something
> generic for PostgreSQL. Even if you only do a moving average function it
> would be a good start.
> --
> Jim C. Nasby, Database Consultant decibel(at)decibel(dot)org
> Give your computer some brain candy! www.distributed.net Team #1828
>
> Windows: "Where do you want to go today?"
> Linux: "Where do you want to go tomorrow?"
> FreeBSD: "Are you guys coming, or what?"
>
> ---------------------------(end of broadcast)---------------------------
> TIP 2: you can get off all lists at once with the unregister command
> (send "unregister YourEmailAddressHere" to majordomo(at)postgresql(dot)org)
>
From | Date | Subject | |
---|---|---|---|
Next Message | Bruno Almeida do Lago | 2005-01-21 19:27:52 | Re: Best Linux Distribution |
Previous Message | Joshua D. Drake | 2005-01-21 19:13:52 | Re: Best Linux Distribution |