Passing the table as a parameter

2019-03-06 14:15发布

问题:

I have to convert from lat and long to geom to use PostGIS. My problem, I have various tables from different locations and I want to pass the table as a parameter to the function. I'm trying this:

CREATE or REPLACE FUNCTION convert_from_lon_lat(float,float,character varying)      
RETURNS  integer AS $$
select id from $3 as vertices 
order by  vertices.geom <-> ST_SetSrid(ST_MakePoint($1,$2),4326) LIMIT 1;
$$ LANGUAGE SQL;

but I get a syntax error.

EDIT1:

So I changed the previous code to this:

CREATE or REPLACE FUNCTION convert_from_lon_lat(long float, lat float, _table character varying) RETURNS  integer AS $$
BEGIN
EXECUTE('select id from _table as vertices order by  vertices.geom <-> ST_SetSrid(ST_MakePoint(long,lat),4326) LIMIT 1;');
END;
$$ LANGUAGE plpgsql;

it creates without any problem, but when I call it `convert_from_lon_lat(long1, long2, my_table)

I get and error:

ERROR:  relation "_table" does not exist

It's not passing the table name as an argument

EDIT 2:

CREATE or REPLACE FUNCTION convert_from_lon_lat(long float, lat float, tbl character varying) RETURNS  integer AS $func$
BEGIN
EXECUTE format('select id from %s order by  %s.the_geom <-> ST_SetSrid(ST_MakePoint('|| long || ','|| lat ||'),4326) LIMIT 1;', tbl, tbl);
END;
$func$ LANGUAGE plpgsql;

Now when I call the function, I get an `ERROR: control reached end of function without RETURN``

I tried RETURN QUERY EXECUTE format('... but I get a ERROR: cannot use RETURN QUERY in a non-SETOF function

回答1:

AS @dezso mentioned, you'll need dynamic SQL in this case.

Dynamic SQL with EXECUTE

So, you're on the right track; forming a dynamic SQL statement using PL/pgSQL, but you just need the finishing touches:

CREATE or REPLACE FUNCTION convert_from_lon_lat(long float, lat float, _table text) 
RETURNS integer AS $$
BEGIN
RETURN QUERY EXECUTE format('SELECT id FROM %I AS vertices 
  ORDER BY vertices.geom <->ST_SetSrid(ST_MakePoint(long,lat),4326) LIMIT 1;',_table);
END
$$ LANGUAGE plpgsql;

I believe this should solve your issues.

Note: We've discovered an error with the above solution and using SETOF, I've attempted to correct the issues below.

EDIT:

A few edits here, hopefully one solution will fix your issue. Also, please excuse any syntax errors in my previous & current solutions; I don't have time to test them right now. :(

1) You could just try returning a SETOF integers, knowing that of course you'll only return the one. Your return type in this case will then be a single, one-column row containing an integer.

CREATE or REPLACE FUNCTION convert_from_lon_lat(long float, lat float, _table text) 
RETURNS SETOF integer AS $$
BEGIN
RETURN QUERY EXECUTE format('SELECT id FROM %I AS vertices 
  ORDER BY vertices.geom <->ST_SetSrid(ST_MakePoint(long,lat),4326) LIMIT 1;',_table);
END
$$ LANGUAGE plpgsql;

and then call as:

SELECT * FROM convert_from_lon_lat(...);

2) To specifically return an integer, I think you can try this:

CREATE or REPLACE FUNCTION convert_from_lon_lat(long float, lat float, _table text) 
RETURNS integer AS $$

DECLARE
return_id integer;

BEGIN
EXECUTE format('SELECT id FROM %I AS vertices 
  ORDER BY vertices.geom <->ST_SetSrid(ST_MakePoint(long,lat),4326) LIMIT 1;',_table)
  INTO return_id;

RETURN return_id;
END
$$ LANGUAGE plpgsql;