Passing the table as a parameter - postgresql

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

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;

Related

problem creating PLpgSQL function which accepts ARRAY as INPUT and returns SETOF RECORD from the table

I'm trying to create function which will accept ARRAY as INPUT and then return SETOF RECORD for each of the parameter in ARRAY.
I have table country_regions which consists of 3 Columns: id int, region_name TEXT, country_name TEXT;
My Functions code looks like this:
CREATE OR REPLACE FUNCTION search1(TEXT[])
RETURNS SETOF RECORD AS $$
DECLARE x RECORD;
BEGIN
FOR x IN
SELECT *
FROM company_regions
WHERE country_name = $1::TEXT
LOOP
RETURN NEXT x;
END LOOP;
END; $$
LANGUAGE plpgSQL;
This Function was created successfully, but when I try to call the function like this:
SELECT * FROM search1(ARRAY ['usa', 'canada']) AS search1(id int, region_name TEXT, country_name text)
it returns table with 0 rows in it.
Can someone tell me what am I doing wrong? I'm completely new to SQL, tried to find answer in other post but I still could not figure out the problem.
You try to compare text value versus text[].
CREATE OR REPLACE FUNCTION search1(text[])
RETURNS SETOF company_regions AS $$
BEGIN
RETURN QUERY SELECT * FROM company_regions
WHERE country_name = ANY($1);
END
$$ LANGUAGE plpgsql STABLE
Attention - functions like this are black box for optimizer. Usually is not too good (from performance perspective) using functions like envelops of one SQL statement. In complex query it can block some optimizations (Mainly if you forget to set correct flag of function - in this case STABLE).

PostgreSQL error in creating function with varying columns using RECORD

I have this function that should be returning a varying columns, can I get a help on what is wrong with my code?
CREATE OR REPLACE FUNCTION functions.search(column_name VARCHAR(40))
RETURNS SETOF RECORD AS $$
DECLARE
rec RECORD;
BEGIN
RETURN QUERY EXECUTE format('SELECT %I FROM students_table);
END;
$$ LANGUAGE plpgsql;
Call:
SELECT * FROM functions.search(
('Student Id', 'Subect')
) as ("Student Id" bigint, "Subect" text)
Here's my error
ERROR: function functions.search(record) does not exist
LINE 1: SELECT * FROM functions.search(
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
SQL state: 42883
Character: 15
Thank you very much
There are several things wrong here.
First, to support a variable number of arguments, you need a VARIADIC parameters. Then, your format function call is somewhat truncated and syntactical nonsense.
You could experiment with a function like this:
CREATE FUNCTION functions.search(VARIADIC column_names text[]) RETURNS SETOF record
LANGUAGE plpgsql AS
$$BEGIN
RETURN QUERY EXECUTE
format('SELECT %s FROM students_table',
(SELECT string_agg(quote_ident(s), ', ')
FROM unnest(column_names) AS cols(s))
);
END;$$;
You can call it like this:
SELECT *
FROM functions.search('Student Id', 'Subect')
AS ("Student Id" bigint, "Subect" text);

Syntax error at or near "unnest"

This request:
unnest('{1,2}'::int[]);
gives to me this error:
syntax error at or near "unnest"
neither unnest('{1,2}'); works
Why?
intire:
CREATE OR REPLACE FUNCTION result() RETURNS setof users AS
$$
DECLARE
BEGIN
unnest('{1,2}'::int[]);
RETURN QUERY SELECT * FROM users;
END;
$$ LANGUAGE plpgsql;
SELECT result();
EDIT
The core idea:
To retrive and manipualate with the bigint[] which is stored inside in a column.
So, i have got this:
SELECT * FROM users WHERE email = email_ LIMIT 1 INTO usr;
Then, usr.chain contains some bigint[] data. For example, {1,2,3,4,5,6,7,8,9,10}. I want to save only the 4 last of them.
How to retrieve {7,8,9,10} and {1,2,3,4,5,6} and iterate over these arrays?
I only found the solution is to use SELECT FROM unnest(usr.chain) AS x ORDER BY x ASC LIMIT (sdl - mdl) OFFSET mchain and so on. but unnest function gives to me this stupid error. I'm really do not understand why it happends. It doesn't work in sucj easy case I wrote at the beginning of the question. subarray function doesn't work because of the data type is bigint[] not int[]
Futher more, the code unnest(ARRAY[1,2]) gives to me the same error.
http://www.postgresql.org/docs/9.2/static/functions-array.html
The same error for array_append function
to iterate over array:
CREATE OR REPLACE FUNCTION someresult(somearr bigint[] ) RETURNS setof bigint AS
$$
DECLARE
i integer;
x bigint;
BEGIN
for x in select unnest($1)
loop
-- do something
return next x;
end loop;
-- or
FOR i IN array_lower($1, 1) .. array_upper($1, 1)
LOOP
-- do something like:
return next ($1)[i];
end loop;
END;
$$ LANGUAGE plpgsql;
select someresult('{1,2,3,4}') ;
array_append ....
CREATE OR REPLACE FUNCTION someresult2(somearr bigint[],val bigint ) RETURNS bigint[] AS
$$
DECLARE
somenew_arr bigint[];
BEGIN
somenew_arr = array_append($1, $2 );
return somenew_arr;
END;
$$ LANGUAGE plpgsql;
select someresult2('{1,2,3,4}' ,222) ;
so, here you have basic example how to iterate and append arrays. Now can you write step by step what you want to do, to achieve .

Rename the column name of a stored function

I've got a postgresql stored procedure, which is returning an integer.
When I call that function, the result is returned with the function name as column name.
For example the name of the function is: "add-person". The column name, when invoking the function, is "add-person".
Is there a way to make the database return the integer with a self-choosen column name? For example "id"?
I think it is pretty easy, but I currently miss the forests for the trees..
Edit:
What i'd missed to tell, is that the return value is a variable, like so:
CREATE OR REPLACE FUNCTION "scheme"."add-person"(arggivenname character varying, argfamilyname character varying) RETURNS integer AS
$BODY$
DECLARE
varResponse integer;
BEGIN
-- Operations before
INSERT INTO "scheme"."table"
(
given_name,
family_name
)
VALUES
(
arggivenname,
argfamilyname
)
RETURNING
"id"
INTO
varResponse;
-- Operations after
RETURN varResponse;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
You can us the AS statement for that. That means:
Select add-person() AS yourcolumnname
To have a named column from a function it is necessary to create a type and return that type from the function
create type mytype as (mycolumn integer);
create or replace function ri()
returns mytype as $$
select 1;
$$ language sql;
select * from ri();
mycolumn
----------
1
Edit
Or much simpler without the type creation as in #pozs comment:
create or replace function ri(out mycolumn integer)
as $$
select 1;
$$ language sql;

Convert string to column name

I'm trying to write a simple postgres function which looks more or less like that:
CREATE OR REPLACE FUNCTION USER_TOTALS(column_name varchar, in_t users) RETURNS float AS $$
DECLARE
sum float;
BEGIN
sum = (SELECT SUM($1) FROM jobs WHERE jobs.technician_id = in_t.id);
RETURN sum;
END;
$$ LANGUAGE plpgsql;
And i need to use it like that:
SELECT users.*, USER_TOTALS('jobs.price', users.*) AS total_price_value FROM users;
Hovewer, that's obviously not working cause SUM() function expects to get a column name but my code passes a varchar to it, so the error says:
Function sum(character varying) does not exist
The question is - can i somehow cast a varchar variable to column name var type? I've been googling for this thing for about 2 hours now and i have no idea how can i make that happen.
A recommended form:
CREATE OR REPLACE FUNCTION USER_TOTALS(column_name varchar, in_t users)
RETURNS float AS $$
DECLARE
sum float;
BEGIN
EXECUTE format('SELECT SUM(%I) FROM jobs WHERE jobs.technician_id=$1', column_name)
INTO sum
USING in_t;
RETURN sum;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION USER_TOTALS(column_name varchar, in_t users) RETURNS float AS $$
DECLARE
sum float;
BEGIN
EXECUTE 'SELECT SUM('||column_name||') FROM jobs WHERE jobs.technician_id='||in_t INTO sum;
RETURN sum;
END;
$$ LANGUAGE plpgsql;