Can't create Postgres procedure from a query - postgresql

I am coming from a mssql world and moving over to postgres. I am trying to create a new procedure from a query I wrote and it fails on creation. I am using pgAdmin 4 to create the proc and I've tried copy-pasting the query into the "code" tab of the dialog box.
What I'm trying to accomplish is inserting a bunch of rows into a table and outputting the ids from the identity column into a temporary table. I will be using those ids for more work further down the line, but it's failing before it is even usable. The way I did it in MSSQL was I had a table variable and used "output inserted.id" to get those values to insert into the table variable.
From what I understand, I have to create a temp table and use the returning keyword in postgres. The following query works if I run it in a query window
CREATE TEMPORARY TABLE temp_table
(
temp_id integer
);
WITH ROWS AS
(
INSERT INTO table_a
(some_name_a)
SELECT some_name_b
FROM table_b
RETURNING id)
INSERT INTO temp_table(temp_id)
SELECT id FROM ROWS;
But when I try to create the procedure for that I get an error saying
"ERROR: syntax error at or near "CREATE" LINE 3: AS $BODY$CREATE TEMPORARY TABLE temp_table^"
Here is what the create proc code looks like:
CREATE OR REPLACE PROCEDURE public.temp()
LANGUAGE 'plpgsql'
AS $BODY$
CREATE TEMPORARY TABLE temp_table
(
temp_id integer
);
WITH ROWS AS
(
INSERT INTO table_a
(some_name_a)
SELECT some_name_b
FROM table_b
RETURNING id)
INSERT INTO temp_table(temp_id)
SELECT id FROM ROWS;
$BODY$;

Related

Delete Duplicate rows in several Postgresql tables

I have a postgres database with several tables like table1, table2, table3. More than 1000 tables.
I imported all of these tables from a script. And apparently the script had issues to import.
Many tables have duplicate rows (all values exactly same).
I am able to go in each table and then delete duplicate row using Dbeaver, but because there are over 1000 tables, it is very time consuming.
Example of tables:
table1
name gender age
a m 20
a m 20
b f 21
b f 21
table2
fruit hobby
x running
x running
y stamp
y stamp
How can I do the following:
Identify tables in postgres with duplicate rows.
Delete all duplicate rows, leaving 1 record.
I need to do this on all 1000+ tables at once.
As you want to automate your deduplication of all table, you need to use plpgsql function where you can write dynamic queries to achieve it.
Try This function:
create or replace function func_dedup(_schemaname varchar) returns void as
$$
declare
_rec record;
begin
for _rec in select table_name from information_schema. tables where table_schema=_schemaname
loop
execute format('CREATE TEMP TABLE tab_temp as select DISTINCT * from '||_rec.table_name);
execute format('truncate '||_rec.table_name);
execute format('insert into '||_rec.table_name||' select * from tab_temp');
execute format('drop table tab_temp');
end loop;
end;
$$
language plpgsql
Now call your function like below:
select * from func_dedup('your_schema'); --
demo
Steps:
Get the list of all tables in your schema by using below query and loop it for each table.
select table_name from information_schema. tables where table_schema=_schemaname
Insert all distinct records in a TEMP TABLE.
Truncate your main table.
Insert all your data from TEMP TABLE to main table.
Drop the TEMP TABLE. (here dropping temp table is important we have to reuse it for next loop cycle.)
Note - if your tables are very large in size the consider using Regular Table instead of TEMP TABLE.

How to return a collection (multiple rows) from a stored procedure in Postgres?

I have the following requirement,
Call a procedure with table of ID's and get the matching records from another table in PG.
Eg:
CALL PROC_A(<input will be table type>, out < table type>)
CREATE PROC_A (in input <some_tbl_type>, inout output <some_tbl_typ>)
AS $$
out = select some columns
from <some_tbl>
where id in (select id from input)
$$;
How to do this in Postgres? i want to use only PROCEDURE not function.
So that i can migrate my existing code to PG.

Import CSV into Postgres: Update & Insert at the same time

So I´m fairly new to Postgresql and started working with it by testing out some stuff with pgadmin4 on Postgres9.6.
The problem:
I have a table: test(id, text)
In this table I have 10 rows of data.
Now I want to import a CSV which has 12 rows to update the test table. Some text changed for the first 10 rows AND I want to insert the 2 additional rows from the CSV.
I know that you can truncate all the data from a table and just import everything again from the CSV, but that´s not a nice way to do this. I want to Update my existing data & Insert the new data with one query.
I already found a function which should solve this by using a temporary table. This updates the existing rows correct, but the 2 additional rows do not get inserted
CREATE OR REPLACE FUNCTION upsert_test(integer,varchar) RETURNS VOID AS $$
DECLARE
BEGIN
UPDATE test
SET id = tmp_table.id,
text = tmp_table.text
FROM tmp_table
WHERE test.id = tmp_table.id;
IF NOT FOUND THEN
INSERT INTO test(id,text) values
(tmp_table.id,tmp_table.text);
END IF;
END;
$$ Language 'plpgsql';
DO $$ BEGIN
PERFORM upsert_test(id,text) FROM test;
END $$;
So what do I need to change to also get the insert to work?
Assuming you have a primary or unique constraint on the id column you can do this with a single statement, no function required:
insert into test (id, text)
select id, text
from tmp_table
on conflict (id)
do update set text = excluded.text;

PostgreSQL (9.4) temporary table scope

After some aggravation, I found (IMO) odd behavior when a function calls another. If the outer function creates a temporary table, and the inner function creates a temporary table with the same name, the inner function "wins." Is this intended? FWIW, I am proficient at SQL Server, and temporary tables do not act this way. Temporary tables (#temp or #temp) are scoped to the function. So, an equivalent function (SQL Server stored procedure) would return "7890," not "1234."
drop function if exists inner_function();
drop function if exists outer_function();
create function inner_function()
returns integer
as
$$
begin
drop table if exists tempTable;
create temporary table tempTable (
inner_id int
);
insert into tempTable (inner_id) values (1234);
return 56;
end;
$$
language plpgsql;
create function outer_function()
returns table (
return_id integer
)
as
$$
declare intReturn integer;
begin
drop table if exists tempTable; -- note that inner_function() also declares tempTable
create temporary table tempTable (
outer_id integer
);
insert into tempTable (outer_id) values (7890);
intReturn = inner_function(); -- the inner_function() function recreates tempTable
return query
select * from tempTable; -- returns "1234", not "7890" like I expected
end;
$$
language plpgsql;
select * from outer_function(); -- returns "1234", not "7890" like I expected
There are no problem with this behaviour, in PostgreSQL temp table can have two scopes:
- session (default)
- transaction
To use the "transaction" scope you should use "ON COMMIT DROP" at the end of the CREATE TEMP statement, i.e:
CREATE TEMP TABLE foo(bar INT) ON COMMIT DROP;
Anyway your two functions will be executed in one transaction so when you call the inner_function from the outer_function you'll be in the same transaction and PostgreSQL will detect that "tempTable" already exists in the current session and will drop it in "inner_function" and create again...
Is this intended?
Yes, these are tables in the database, similar to permanent tables.
They exist in a special schema, and are automatically dropped at the end of a session or transaction. If you create a temporary table with the same name as a permanent table, then you must prefix the permanent table with its schema name to reference it while the temporary table exists.
If you want to emulate the SQL Server implementation then you might consider using particular prefixes for your temporary tables.

Create a temporary table from a selection or insert if table already exist

How to create a temporary table, if it does not already exist, and add the selected rows to it?
CREATE TABLE AS
is the simplest and fastest way:
CREATE TEMP TABLE tbl AS
SELECT * FROM tbl WHERE ... ;
Do not use SELECT INTO. See:
Combine two tables into a new one so that select rows from the other one are ignored
Not sure whether table already exists
CREATE TABLE IF NOT EXISTS ... was introduced in version Postgres 9.1.
For older versions, use the function provided in this related answer:
PostgreSQL create table if not exists
Then:
INSERT INTO tbl (col1, col2, ...)
SELECT col1, col2, ...
Chances are, something is going wrong in your code if the temp table already exists. Make sure you don't duplicate data in the table or something. Or consider the following paragraph ...
Unique names
Temporary tables are only visible within your current session (not to be confused with transaction!). So the table name cannot conflict with other sessions. If you need unique names within your session, you could use dynamic SQL and utilize a SEQUENCE:
Create once:
CREATE SEQUENCE tablename_helper_seq;
You could use a DO statement (or a plpgsql function):
DO
$do$
BEGIN
EXECUTE
'CREATE TEMP TABLE tbl' || nextval('tablename_helper_seq'::regclass) || ' AS
SELECT * FROM tbl WHERE ... ';
RAISE NOTICE 'Temporary table created: "tbl%"' || ', lastval();
END
$do$;
lastval() and currval(regclass) are instrumental to return the dynamically created table name.