[vox-tech] postgres: run function on each row returned from
select statement
Dylan Beaudette
dylan.beaudette at gmail.com
Thu Apr 26 08:40:40 PDT 2007
On 4/23/07, Harold Lee <harold at hotelling.net> wrote:
> Dylan Beaudette wrote:
> > Is there any way to run a function, which expects a single value as an
> > argument, for each row returned from a select statement? Note that this is a
> > special function, used in the following way:
> >
> > SELECT sum(length(the_geom )) as dist_meters
> > FROM
> > shortest_path_as_geometry('roads', 2192, 154) ;
> >
> > In the above example the function "shortest_path_as_geometry" expects three
> > arguments... Is there any way to feed a function like this its arguments from
> > column returned from a previous select statement? Or, would this function
> > have to be re-written to allow for this flexibility?
> >
> > thanks in advance,
> >
> >
>
> You question isn't 100% clear to me, but I'm not going to let that stop
> me from opining :-)
>
> Sounds like you have some query that returns (string, roadid1, roadid2)
> and you want to call the shortest_path_as_geometry function on each row
> that comes back from that, then sum the length of those geometries.
>
> Approach #1:
>
> Change the sum_path_as_geometry function to take an array or row instead
> of three value and move the shortest_path_as_geometry function into the
> SELECT list, e.g.
>
> SELECT sum(length(sum_path_as_geometry(a.c1, a.c2, a.c3)))
> FROM ( SELECT c1, c2, c3 FROM ... ) a;
>
> Approach #2:
>
> Glue the other query and sum_path_as_geometry together with a new table
> function (i.e. a function that returns a set of rows). Define the new
> function to apply the sum_path_as_geometry function to the appropriate
> rows and then use the new function in your query:
>
> SELECT sum(length(the_geom)) FROM new_function();
>
>
Thanks for the ideas Harold. Here is the actual function definition,
it would be nice to make the changes that you suggested: allow this
function to work on rows of data instead of a single set of 3 values.
-----------------------------------------------------------------------
-- Compute the shortest path using edges and vertices table, and return
-- the result as a set of (gid integer, the_geom gemoetry) records.
-----------------------------------------------------------------------
CREATE OR REPLACE FUNCTION shortest_path_as_geometry(geom_table
varchar, geom_source anyelement,
geom_target
anyelement) RETURNS SETOF GEOMS AS
$$
DECLARE
r record;
source int4;
target int4;
path_result record;
v_id integer;
e_id integer;
geom geoms;
BEGIN
FOR r IN EXECUTE 'SELECT id FROM ' || quote_ident(geom_table)
|| '_vertices WHERE geom_id = ' || quote_literal(geom_source) LOOP
source = r.id;
END LOOP;
IF source IS NULL THEN
RAISE EXCEPTION 'Can''t find source edge';
END IF;
FOR r IN EXECUTE 'SELECT id FROM ' || quote_ident(geom_table)
|| '_vertices WHERE geom_id = ' || quote_literal(geom_target) LOOP
target = r.id;
END LOOP;
IF target IS NULL THEN
RAISE EXCEPTION 'Can''t find target edge';
END IF;
FOR geom IN SELECT * FROM
shortest_path_as_geometry_internal_id(geom_table, source, target) LOOP
RETURN NEXT geom;
END LOOP;
RETURN;
END;
$$
LANGUAGE 'plpgsql' VOLATILE STRICT;
the above function then calls another function, which actually does
the work, but instead using internal id numbers:
CREATE OR REPLACE FUNCTION
shortest_path_as_geometry_internal_id(geom_table varchar,
source int4, target int4)
RETURNS SETOF GEOMS AS
$$
DECLARE
r record;
path_result record;
v_id integer;
e_id integer;
geom geoms;
BEGIN
FOR path_result IN EXECUTE 'SELECT vertex_id, edge_id FROM
shortest_path(''SELECT id, source, target, cost FROM ' ||
quote_ident(geom_table) || '_edges '', ' ||
quote_literal(source) || ' , ' || quote_literal(target) || ' , false,
false) ' LOOP
v_id = path_result.vertex_id;
e_id = path_result.edge_id;
FOR r IN EXECUTE 'SELECT gid, the_geom FROM ' ||
quote_ident(geom_table) || ' WHERE edge_id = ' || quote_literal(e_id)
LOOP
geom.gid := r.gid;
geom.the_geom := r.the_geom;
RETURN NEXT geom;
END LOOP;
END LOOP;
RETURN;
END;
$$
LANGUAGE 'plpgsql' VOLATILE STRICT;
Can these type of functions easily be generalized to work on a rows of
data instead of what they currently use?
any ideas would be greatly appreciated!
PS: these functions are part of a shortest path routing solution for
PostGIS, the spatial extension to PostgreSQL.
Cheers,
Dylan
More information about the vox-tech
mailing list