3

I have a PostgreSQL 9.1 database that stores a number of "areas" with the borders and I need to work out the effective border for an area. Some of the areas store a border directly (as a PostGIS geometry), while others are composed of multiple child areas, which need to be aggregated together.

For each child are there is an "operation" that determines whether its added to the previous one or subtracted from it or intersected with it. This means that the order matters, too, so there is a sequence number.

I have an aggregate function that work this out, but the problem is that the structure is recursive - a child area may itself be composed of child areas.

A simplified schema:

CREATE TABLE area
    (id integer NOT NULL
    , border geometry NULL
    );
CREATE TABLE area_part
    (parent_area_id integer NOT NULL
    , sequence integer NOT NULL
    , operation text NOT NULL
    , child_area_id integer NOT NULL
    );

Aggregate function signature (it expects the rows ordered by sequence):

CREATE AGGREGATE aggregate_geometry(area geometry, operation text)
-- RETURNS geometry

I've created a normal PL/pgSQL function that calls itself recursively and it works, but it's slow, because it performs many sub-queries. Any ideas on how this can be done more efficiently?

I've also tried writing a query with a recursive CTE:

WITH RECURSIVE area_rec AS
(
    SELECT *
    FROM area
    WHERE id = the_if_of_interest

    UNION ALL

    SELECT c.*
    FROM area_rec rec
    JOIN area_part p ON rec.id = p.parent_area_id
    JOIN area c ON p.child_area_id = c.id
)
SELECT *
FROM area_rec

That's fine for returning all the rows needed for a given area, but I don't know how to then plug the values into my aggregate function. I need some kind of an "aggregate recursive function" here!

11
  • Do you have the code for the geometry aggregation function? Commented Nov 30, 2011 at 13:31
  • What do you want to restrict? The begin/end points? The total hopcount? Commented Nov 30, 2011 at 13:51
  • You can think of it as ST_Union. The real function is more complex and uses some other stuff not directly relevant to the question, but it takes in a set of geometries and returns a geometry, just like the aggregate version of ST_Union. Commented Nov 30, 2011 at 13:52
  • Is it a scalar function, needing only scalar arguments, or does it need one row of data, or maybe even more than one row of data? Commented Nov 30, 2011 at 13:55
  • Can't you just use your function in the (outer) SELECT part of the CTE? Commented Nov 30, 2011 at 13:56

1 Answer 1

2

You really have to approach this sort of problem in two phases. The first is to create a data set you can aggregate across (recursion, the CTE) and the second is the aggregation. The approach for the aggregation would seem to me to be a window function. Then if you need to you could include this inside another CTE for further post-processing. Remember, CTE's can be nested to an arbitrary depth but you shouldn't need more than two levels for this. You do want to keep this as simple as possible.

Without the function I don't really know what the query would look like but this should be enough to get your started.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.