[postgis-users] using dynamic tables in plpgsql nearest neighbour query

Ben Madin lists at remoteinformation.com.au
Fri Apr 23 07:10:52 PDT 2010


G'day all,

I am trying to sort out a routing problem over much of South-East Asia, and trying to fill gaps using a number of different data sources.

I have a plpgsql function which works - with a static table name etc. When I tried to change it to a dynamic name using the execute 'query' using variables; statement, it doesn't work.

I am suspicious that the geometry type might be the cause

Anyways, this works : (using a multilinestring table with vertices assigned using the pgrouting function.

CREATE OR REPLACE FUNCTION find_nearest_road(tabname varchar,
    point geometry, sf varchar(6), OUT value int
) AS
$BODY$
DECLARE
    max_search_radius real := 5.0; -- this is assuming working in degrees I guess!
    search_radius real := 0.01;
    rec record; -- this has to match your lookup table
BEGIN
LOOP
    SELECT gid, source, target INTO rec
    FROM road m -- and you might want to change this
    WHERE st_expand(point, search_radius) && m.the_geom
    ORDER BY st_distance(point, m.the_geom)
    LIMIT 1;
    IF FOUND THEN -- you could really simplify this, it might speed it up, but I kind of like it.
        IF substring(sf from 1 for 1) iLIKE 's' --start or Source
            THEN
            value := rec.source;
        ELSIF substring(sf from 1 for 1) iLIKE 't' --target
           OR substring(sf from 1 for 1) iLIKE 'f' --finish
           THEN
            value := rec.target;
        ELSIF substring(sf from 1 for 1) iLIKE 'g' --gid
           OR substring(sf from 1 for 1) iLIKE 'i' --id
            THEN
            value := rec.gid;
        END IF;
        EXIT;
    END IF;
    search_radius := search_radius * 2.0;
    EXIT WHEN search_radius > max_search_radius;
END LOOP;
END;
$BODY$ LANGUAGE plpgsql STABLE STRICT;


and this doesn't :-  the first 5 lines after loop above have been replaced with the 7 lines below, but I haven't even quoted the tablename - it's still hardcoded.

CREATE OR REPLACE FUNCTION find_nearest_road(tabname varchar,
    point geometry, sf varchar(6), OUT value int
) AS
$BODY$
DECLARE
    max_search_radius real := 5.0; -- this is assuming working in degrees I guess!
    search_radius real := 0.01;
    rec record; -- this has to match your lookup table
BEGIN
LOOP
    EXECUTE 'SELECT gid, source, target
    FROM road m -- and you might want to change this
    WHERE st_expand($1, $2) && m.the_geom
    ORDER BY st_distance($1, m.the_geom)
    LIMIT 1'
     INTO rec
     USING point, search_radius;
    IF FOUND THEN -- you could really simplify this, it might speed it up, but I kind of like it.
        IF substring(sf from 1 for 1) iLIKE 's' --start or Source
            THEN
            value := rec.source;
        ELSIF substring(sf from 1 for 1) iLIKE 't' --target
           OR substring(sf from 1 for 1) iLIKE 'f' --finish
           THEN
            value := rec.target;
        ELSIF substring(sf from 1 for 1) iLIKE 'g' --gid
           OR substring(sf from 1 for 1) iLIKE 'i' --id
            THEN
            value := rec.gid;
        END IF;
        EXIT;
    END IF;
    search_radius := search_radius * 2.0;
    EXIT WHEN search_radius > max_search_radius;
END LOOP;
END;
$BODY$ LANGUAGE plpgsql VOLATILE STRICT;

the first function returns :

prices=# select find_nearest_road('road','0101000020E610000052FC3DCF94A459409734BBCFC2243240'::geometry,'s');
find_nearest_road
-------------------
        1507
(1 row)

and the second version of the function :

prices=# select find_nearest_road('road','0101000020E610000052FC3DCF94A459409734BBCFC2243240'::geometry,'s');
find_nearest_road
-------------------
(1 row)

So I guess it comes down to what's wrong between :

SELECT gid, source, target INTO rec  FROM road m WHERE st_expand(point, search_radius) && m.the_geom ORDER BY st_distance(point, m.the_geom) LIMIT 1;


EXECUTE 
'SELECT gid, source, target FROM road m WHERE st_expand($1, $2) && m.the_geom ORDER BY st_distance($1, m.the_geom) LIMIT 1'
INTO rec USING point, search_radius;

Any advice gratefully received,

Ben





More information about the postgis-users mailing list