In a plpgsql procedure I am looking how to reference and use a result set that I get from the first query. Following code tries to demonstrate what I want to achieve:
do
$body$
DECLARE
ref_result_set ???;
BEGIN
ref_result_set := select 'asdf';
perform xxx from ref_result_set;
perform yyy from ref_result_set;
END;
$body$
language plpgsql;
I was looking at cursors but there is just an option to fetch row by row and not an entire set. Is there any option how to achieve this without first writing to a table?
Question asked
There are no "table variables" in plpgsql (or SQL). You can use:
cursors
temporary, unlogged or regular tables
the original query as subquery, or a function or view doing the same
CTEs (for the scope of a single SQL statement)
Related questions:
Select from a table variable
Function to return a table of all children of a node
Actual problem
For your actual problem I suggest data-modifying CTEs:
WITH sel AS (
SELECT col1, col2, ..
FROM tbl1
WHERE <expensive condition>
)
, ins1 AS (
INSERT INTO test1 (col1, col2, ..)
SELECT col1, col2, ..
FROM sel
WHERE <some condition>
)
INSERT INTO test2 (col1, col2, ..)
SELECT col1, col2, ..
FROM sel
WHERE <some condition>;
You can use that inside plpgsql code or as standalone SQL command.
Inside plpgsql code you can reference variables in the query ...
Related
I have the function in which I have prepared dynamic query, which I want print in output window before executing it.
Note: In the following example I have just add simple select statement to understand the requirement.
Sample tables:
create table t1
(
col1 int,
col2 text
);
insert into t1 values(1,'Table T1');
insert into t1 values(2,'Table T1');
create table t2
(
col1 int,
col2 text
);
insert into t2 values(1,'Table T2');
insert into t2 values(2,'Table T2');
Function:
create or replace function fn_testing(tbl_Name text)
returns table(col1 int,col2 text) as
$$
begin
return query execute 'select col1,col2 from '||tbl_name||'';
end;
$$
language plpgsql;
Function call:
select * from fn_testing('t2');
I want to print following in message window with result set too in result window:
select col1,col2 from t1;
You can use RAISE NOTICE for messages.
CREATE OR REPLACE FUNCTION fn_testing
(_tbl_name name)
RETURNS TABLE
(col1 integer,
col2 text)
AS
$$
DECLARE
_query text;
BEGIN
_query := format('SELECT col1, col2 FROM %I;', _tbl_name);
RAISE NOTICE '%', _query;
RETURN QUERY EXECUTE _query;
END;
$$
LANGUAGE plpgsql;
Note: There's a special type, name, for identifiers. And to prevent SQL injection or errors you should make sure the dynamic identifiers are properly quoted. You can use format() with %I for that.
I have a table tab1 with four columns col1, col2, col3 and col4.
I want to create a function like f4(a) where a is defined by user and if user types select f4(col1) he gets column tab1.col1.
Is there any way to create such function in PostgreSQL?
There is really not good reason to complicate matters with a function here.
What you should do instead:
SELECT col1 FROM tab1;
What you ask for:
CREATE OR REPLACE FUNCTION f4(_col text)
RETURNS TABLE (col_x text)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE
format('SELECT %I FROM tab1', _col);
END
$func$;
Call:
SELECT * FROM f4('col1');
You need dynamic SQL because SQL does not allow to parameterize identifiers.
Further reading:
Using variable for fieldname in postgresql
Define table and column names as arguments in a plpgsql function?
I found many information about this topic, but nothing I can really utilize for my function.
I created a trigger-function in PostsgreSQL 9.6.0 which works fine when I use static variables. Anyway, because I or other people will use the script later ,I want keep it as easy as possible to adapt it on a new environment by changing needed variables in the head of the document. The structure of my function can be described with:
CREATE OR REPLACE FUNCTION userinput()
RETURNS TRIGGER AS
$func$
DECLARE
tblname TEXT := 'products';
trgtcol TEXT := 'col1, col2, col4';
BEGIN
SELECT trgtcol FROM tblname;
END;
$func$ language plpgsql;
How can I now set set an Alias for:
The name of the table 'products' represented by the alias tblname?
A varying number of columns after the select statement?
I know the code above doesn't work but using provided declarations I expect this outcome:
SELECT col1, col2, col4 FROM products
You need dynamic SQL. To be safe from SQL injection, use something like this:
DECLARE
tblname text := 'products';
trgtcol text := 'col1, col2, col4';
sanitized_col text;
BEGIN
/* convert the columns to something safe */
SELECT string_agg(
quote_ident(c.name),
', '
) INTO sanitized_col
FROM regexp_split_to_table(trgtcol, ', *') AS c(name);
EXECUTE format('SELECT %s FROM %I', sanitized_col, tblname)
INTO ...
END;
I have the below function compiled successfully. When I do select schema.funtion_name();, the function gets executed but there are no rows inserted in the table schema.table_insert:
CREATE OR REPLACE FUNCTION schema.function_name()
RETURNS void AS
$BODY$
DECLARE cur_1 CURSOR FOR
Select col1 from schema.table1
union
select col1 from schema.table2
union
select col1 from schema.table3
union
select col1 from schema.table4;
BEGIN
FOR rec_i in cur_1 LOOP
insert into schema.table_insert (col1,col2,col3)
select col1,col2,col3
from schema.view
where col1=rec_i.col1
commit;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql STABLE
The select in cursor cur_1 returns more than 900 000 records. When I use the insert statement separately for single record, the record gets inserted in the table.
I have the below function compiled successfully.
No, you haven't.
For starters, plpgsql functions are not "compiled". On creation, only superficial syntax checks are done, then the function body is stored as is. No compilation. Late binding. Nested SQL statements are treated as prepared statements.
That aside, the function you display cannot be created at all. It is syntactical nonsense. Missing semicolon after the INSERT. COMMIT does not make sense and is not allowed in plpgsql. You do not need a cursor for this. Nor looping. Use a simple SQL statement:
INSERT INTO schema.table_insert (col1, col2, col3)
SELECT v.col1, v.col2, v.col3
FROM schema.view v
JOIN (
SELECT col1 FROM schema.table1
UNION
SELECT col1 FROM schema.table2
UNION
SELECT col1 FROM schema.table3
UNION
SELECT col1 FROM schema.table4;
) sub USING (col1);
Equivalent, may be faster:
INSERT INTO schema.table_insert (col1, col2, col3)
SELECT v.col1, v.col2, v.col3
FROM schema.view v
WHERE EXISTS (SELECT 1 schema.table1 WHERE col1 = v.col1)
OR EXISTS (SELECT 1 schema.table2 WHERE col1 = v.col1)
OR EXISTS (SELECT 1 schema.table3 WHERE col1 = v.col1)
OR EXISTS (SELECT 1 schema.table4 WHERE col1 = v.col1);
Can be wrapped up in a function, but plpgsql is overkill. And STABLE, would be wrong for a function containing an INSERT. I suggest a plain SQL function and VOLATILE is the default and correct for this.
CREATE OR REPLACE FUNCTION schema.function_name()
RETURNS void AS
$func$
INSERT ...
$func$ LANGUAGE sql;
I have an old MSSQL procedure that needs to be ported to a PostgreSQL function. Basically the SQL procedure consist in a CURSOR over a select statement. For each cursor entity i have three select statements based on the current cursor output.
FETCH NEXT FROM #cursor INTO #entityId
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT * FROM table1 WHERE col1 = #entityId
SELECT * FROM table2 WHERE col2 = #entityId
SELECT * FROM table3 WHERE col3 = #entityId
END
The tables from the SELECT statements have different columns.
I know that the PostgreSQL use refcursor in order to return multiple result sets but the question is if is possible to open and return multiple dynamic refcursors inside of a loop?
The Npgsql .NET data provider is used for handling the results.
Postgres test code with only 1 cursor inside loop:
CREATE OR REPLACE FUNCTION "TestCursor"(refcursor)
RETURNS SETOF refcursor AS
$BODY$
DECLARE
entity_id integer;
BEGIN
FOR entity_id IN SELECT "FolderID" from "Folder"
LOOP
OPEN $1 FOR SELECT * FROM "FolderInfo" WHERE "FolderID" = entity_id;
RETURN NEXT $1;
CLOSE $1;
END LOOP;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
Then the test code:
BEGIN;
SELECT * FROM "TestCursor"('c');
FETCH ALL IN c;
COMMIT;
The SELECT * FROM "TestCursor"('c'); output is like on screenshot:
Then when i try to fetch data i get the error: ERROR: cursor "c" does not exist
You can emulate it via SETOF refcursor. But it is not good idea. This T-SQL pattern is not supported well in Postgres, and should be prohibited when it is possible. PostgreSQL support functions - function can return scalar, vector or relation. That is all. Usually in 90% is possible to rewrite T-SQL procedures to clean PostgreSQL functions.