I am trying to execute dynamic query using PostgreSQL. I have a function with three parameters and need to suffix that parameters with some variables(to make view name) and need to retrieve rows from that variables(view) and return the result.
Example
create or replace function testing(abc varchar,def varchar,ghi varchar)
returns setof record as
$BODY$
Declare
temptable1 varchar :='temp1_';
temptable2 varchar :='temp2_';
viewname varchar :='view_';
Body
temptable1 := temptable1||abc;
temptable2 := temptable2||def;
viewname := viewname||ghi;
execute 'Drop table if exists'||temptable1;
execute 'Drop table if exists'||temptable2;
WITH cm
AS
(
SELECT "ssno","rlno",
DENSE_RANK() OVER(Partition by "ssno" Order By "rlno" )FoundIn
From viewname;
)
SELECT DISTINCT * INTO temptable1
FROM cm
WHERE FoundIn > 1;
SELECT DISTINCT cr."ssno", cdr."rlno"
INTO temptable2
FROM temptable1 l1
INNER JOIN viewname cr on l1."rlno" = cr."rlno"
ORDER BY "rlno";
/* Need to result should be display for below query */
SELECT DISTINCT cr.ssno AS Nos, cr.rlno, FoundIn,cr.Name, cr.Address,
from temptable1 l1
inner join viewname cr on l1.rlno = cr.rlno
order by "rlno"
end;
$BODY$
Language plpgsql;
Related
Is there any way to replace the table name in a query with value stored in another table ? This is in postgres sql
Eg
Meta_table
col1 | col 2
Table 1 | val1
Table 2 | val2
My requirement
select *
from (select col1 from meta_table where col2 = val2)
Probably the most flexible and efficient way is dynamically creating a temporary view using function:
create or replace function f_prepare(tname text, vname text) returns text language plpgsql as $$
begin
execute format(
'create or replace temporary view %I as select * from %I',
vname, tname);
return vname;
end $$;
Then you can use the created view in usual way, for example;
select f_prepare('pg_class', 'v_class');
select * from v_class where relname = 'pg_database'; -- Index on the source table will be used here
and using your code:
select f_prepare((select col1 from meta_table where col2 = 'val2'), 'v');
select * from v;
And as any other temporary objects, created views will not conflict with other sessions and will be dropped on disconnect.
If you want to change the table name of a table, then you can just update the relname column in table pg_class.
But for this, you need admin access to the Postgresql.
The query goes like:-
update pg_class set relname='new_table_name' where relname='old_table_name';
So to do this in single line, You can do like this:
update pg_class set relname=(select col1 from meta_table where col2 = val2) where relname='old_table_name';
You can use Do statement with cursor:
Try This:
DO $$
DECLARE
_query text;
_cursor CONSTANT refcursor := '_cursor';
BEGIN
_query := 'SELECT * FROM '|| (select col1 from meta_table where col2 = 'val1');
OPEN _cursor FOR EXECUTE _query;
END
$$;
FETCH ALL FROM _cursor;
I have a query that I use in different parts of a system. Now I want to make a function wherein it accepts a text to be used as a query, I'm not even sure if this is possible. However, I want to know if there's a possible workaround for this.
What I want to achieve is a function that returns a table and accepts a text/varchar that could be used as a query.
Here's what I have, I have a query that is kind of a "base query" that can have different data based on the given CTE which is named:
data_table
refer to the function and its usage below - this is mostly abstract but imo this should be enough.
CREATE OR REPLACE FUNCTION func(
data_query TEXT
)
RETURNS TABLE
(
id BIGINT,
random_text varchar
)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
with data_table AS (
data_query
), another_data_table AS (
SELECT
*
FROM my_data
)
SELECT
d.id,
ad.random_text
FROM data_table d
INNER JOIN another_data_table ad
ON ad.ref_id = d.id;
END; $function$;
usage:
SELECT * FROM func('SELECT * FROM my_data_table');
SELECT * FROM func('SELECT * FROM my_second_data_table');
Instead of passing the query, you may pass the table name and access it dyamically in your query using EXECUTE format
CREATE OR REPLACE FUNCTION func(
table_name_in TEXT
)
RETURNS TABLE
(
id BIGINT,
random_text varchar
)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY EXECUTE format (
'SELECT
d.id :: bigint,
ad.random_text :: varchar
FROM %I d
INNER JOIN my_data ad
ON ad.ref_id = d.id', table_name_in );
END
$function$;
usage:
SELECT * FROM func('my_data_table');
SELECT * FROM func('my_second_data_table');
I want to select a column from a table, with the column name being the result of a query like the following:
-- This query returns a single value
with x as (
select a from table1 where <condition>
)
-- my_function() yields a table
select x from my_function()
How do I do that?
Thank you very much.
You could write it in SQL with a temporary function:
CREATE FUNCTION pg_temp.tablefunc()
RETURNS SETOF my_function_result_type
LANGUAGE plpgsql AS
$$DECLARE
v_colname text;
BEGIN
SELECT a INTO v_colname
FROM table1
LIMIT 1;
RETURN QUERY EXECUTE
format(E'SELECT %I\n'
'FROM my_function()',
v_colname);
END;$$;
SELECT * FROM pg_temp.tablefunc();
I have a parent table layer_1_ and a number of child tables layer_1_points, layer_1_linestrings etc. which contain some geometry data. Each child table has its own geometry constraint. So, for example, layer_1_points has this constraint:
CONSTRAINT enforce_geotype_geom_geom CHECK (geometrytype(geom) = 'POINT'::text)
Whereas layer_1_linestrings table has this constraint:
CONSTRAINT enforce_geotype_geom_geom CHECK (geometrytype(geom) = 'LINESTRING'::text)
Many other layer tables have similar names: layer_2_, layer_3_, ..., layer_N_. And all of them have their own child tables. What I want to achive is that when a user inserts to a parent table (layer_N_), then this insert statement should be forwarded to a particular child table (layer_N_points etc.). So, for example, when I do:
INSERT INTO layer_1_ (geom) VALUES(ST_GeomFromText('POINT(0 0)', 3857))
I should actually insert to layer_1_points, because geom type is POINT. To achive all this I created this trigger function and the trigger itself:
CREATE OR REPLACE FUNCTION trigger_layer_insert()
RETURNS trigger AS
$$
DECLARE
var_geomtype text;
table_name text;
layer_id text := (TG_ARGV[0])::text;
BEGIN
var_geomtype := geometrytype(NEW.geom);
IF var_geomtype = 'POINT' THEN
table_name := (SELECT concat ('layer_', layer_id, '_points'));
ELSIF var_geomtype = 'MULTIPOINT' THEN
table_name := (SELECT concat ('layer_', layer_id, '_multipoints'));
ELSIF var_geomtype = 'LINESTRING' THEN
table_name := (SELECT concat ('layer_', layer_id, '_linestrings'));
ELSIF var_geomtype = 'MULTILINESTRING' THEN
table_name := (SELECT concat ('layer_', layer_id, '_multilinestrings'));
ELSIF var_geomtype = 'POLYGON' THEN
table_name := (SELECT concat ('layer_', layer_id, '_polygons'));
ELSIF var_geomtype = 'MULTIPOLYGON' THEN
table_name := (SELECT concat ('layer_', layer_id, '_multipolygons'));
END IF;
EXECUTE '
INSERT INTO ' || table_name || '
SELECT * FROM (SELECT NEW.*) AS t
';
RETURN NULL;
END;
$$
LANGUAGE 'plpgsql' VOLATILE;
CREATE TRIGGER trigger_layer_1_ BEFORE INSERT
ON layer_1_ FOR EACH ROW
EXECUTE PROCEDURE trigger_layer_insert(1);
However, when I do actual insert like:
INSERT INTO layer_1_ (geom) VALUES(ST_GeomFromText('POINT(0 0)', 3857))
I get an error message:
ERROR: missing FROM-clause entry for table "new"
LINE 3: SELECT * FROM (SELECT NEW.*) AS t
^
QUERY:
INSERT INTO layer_1_points
SELECT * FROM (SELECT NEW.*) AS t
So, what is wrong with SELECT NEW.* and how can I fix it? Thanks!
EDIT
I also tried this:
EXECUTE '
INSERT INTO ' || table_name || '
SELECT * FROM (SELECT NEW.*) AS t
' USING NEW;
But it has no effect.
When you execute something using PLPGSQL statement EXECUTE it runs in the different context so local variables is not visible there. To pass variable(s) the EXECUTE '<SQL script>' USING <variables list>; form is used:
EXECUTE 'insert into table(field1, field2) values ($1, $2)' USING var1, var2;
So the statement should be:
EXECUTE 'INSERT INTO ' || table_name || ' SELECT * FROM SELECT $1.*) AS t'
USING NEW;
But much more secure is using format function:
execute format('INSERT INTO %I SELECT * FROM SELECT $1.*) AS t', table_name)
I have the following function:
In which I am updating one database table by joining other database table by using the dblink().
I have installed:
create extension dblink;
The more details as shown below:
CREATE OR REPLACE FUNCTION Fun_test
(
Table_Name varchar
)
RETURNS void AS
$BODY$
DECLARE
dynamic_statement varchar;
BEGIN
perform dblink_connect('port=5234 dbname=testdb user=postgres password=****');
dynamic_statement := 'With CTE AS
(
Select HNumber,JoiningDate,Name,Address
From '|| Table_Name ||'c
)
, Test_A
AS
(
Select Row_Number() over ( Partition by PNumber order by Date1 Desc,Date2 Desc) AS roNum,
Name,PNumber,Date1,Address
From dblink(
''Select distinct PNumber,
(
case when fname is null then '' else fname end || '' ||
case when lname is null then '' else lname end
) as FullName,
Address,
Date1,Date2
From testdb_Table
inner join CTE on CTE.HNumber = PNumber''
) Num
)
Update CTE
Set
Name = Test_A.FullName
,SubAddress_A = Test_A.Address
,Date1 = Test_A.Date1
from CTE
left outer join Test_A on
CTE.HNumber= Test_A.PNumber
where roNum =1';
RAISE INFO '%',dynamic_statement;
EXECUTE dynamic_statement;
perform dblink_disconnect();
END;
$BODY$
LANGUAGE PLPGSQL;
Calling Function:
select fun_test('test1');
Getting an error:
ERROR: a column definition list is required for functions returning "record"
LINE 11: From dblink
^
You have to tell PostgreSQL what the columns the dblink query will return are.
See the manual for dblink for details.
This is the same as for any function returning a runtime-determined record type. You can't query it without telling PostgreSQL what the column layout of the results will be.
You use a column specifier list, e.g.
SELECT * FROM my_function_returning_record() f(col1 text, col2 integer);
If you are on a current PostgreSQL version you may want to look at postgres_fdw as an alternative to dblink.