inserting record from one table to another using record in postgres for loop - postgresql

I'm trying to insert data from one table to another in postgres using for...loop. The approach is given below.
DO LANGUAGE PLPGSQL $$
DECLARE
data record;
BEGIN
FOR data IN SELECT * FROM forall_data
LOOP
INSERT INTO for_loop values data;<br>
END LOOP;
END;
$$
I've used record for the row iteration but couldn't find out how to insert that 'data' into 'for_loop' table. When I run this code it gives me the following error:
ERROR: syntax error at or near "data"
LINE 9: INSERT INTO for_loop values data;
^
Here are my two tables.
create table forall_data(
nid numeric(15,0)not null,
name varchar(15) not null,
city varchar(10) not null,
contact numeric(11,0) not null
);
create table for_loop(
nid numeric(15,0)not null,
name varchar(15) not null,
city varchar(10) not null,
contact numeric(11,0) not null
);
What should I try here to insert that 'data' record into 'for_loop' table? Thanks in advance.

'data' is untyped record, so I have to mention the column name to retrieve the value of this record.
DO LANGUAGE PLPGSQL $$
DECLARE
data record;
BEGIN
FOR data IN SELECT * FROM forall_data
LOOP
INSERT INTO for_loop values (data.nid,data.name,data.city,data.contact);
END LOOP;
END;
$$
But using %rowtype or table type is more flexible and no need to mention the column names to retrieve column value from the variable
DO LANGUAGE PLPGSQL $$
DECLARE
data forall_data; --- or data forall_data%rowtype
BEGIN
FOR data IN SELECT * FROM forall_data
LOOP
INSERT INTO for_loop select (data).*;
END LOOP;
END;
$$
cheers :)

use this code:
DO LANGUAGE PLPGSQL $$
DECLARE
rec record;
BEGIN
FOR rec IN SELECT * FROM budzet.forall_data
LOOP
INSERT INTO budzet.for_loop(nid, name , city , contact)
VALUES (rec.nid, rec.name , rec.city , rec.contact);
END LOOP;
END;
$$

You can try Loop with some exit condition.
DO LANGUAGE PLPGSQL $$
DECLARE
rec CURSOR FOR SELECT * FROM forall_data;
V_nid numeric;
V_name varchar(15);
V_city varchar(10);
V_contact numeric;
BEGIN
OPEN rec;
LOOP
FETCH rec INTO V_nid ,V_name ,V_city,V_contact;
EXIT WHEN(rec IS NULL);
INSERT INTO for_loop(nid, name , city , contact)
VALUES (V_nid , V_name , V_city , V_contact);
END LOOP;
CLOSE rec;
END;
$$
Hope it work for you.
EDIT: Alternately you can try this without using loop insert statement from one table and select statement from another table.
INSERT INTO for_loop(nid, name , city , contact)
select nid, name , city , contact FROM forall_data;

Related

How avoid extra quotes added within a loop in postgresql

When performing a loop on a table in which a value has ", I get those quotes doubled such as:
initial input: 'test AND "test"'
output in the loop: 'test AND ""test""'
How to reproduce:
CREATE TABLE roles(
id int,
criteria VARCHAR (255)
);
INSERT INTO roles (id, criteria) VALUES (1, 'test AND "test"');
CREATE OR REPLACE FUNCTION test_quote()
RETURNS VARCHAR
LANGUAGE plpgsql
AS $$
DECLARE
sample varchar;
rec record;
BEGIN
FOR rec IN
SELECT * FROM roles
LOOP
sample:= rec;
END LOOP;
RETURN sample;
END
$$;
SELECT * FROM test_quote();
Expected result: (1,"test AND "test"")
Actual result: (1,"test AND ""test""")
Does anyone have an idea how to get the expected behaviour here?
Found it, indeed, the issue is from the conversion from record to varchar.
I can directly pass the column from the rec like:
sample:= rec.criteria;
(I didn't know that we could do that)
So, that gives:
CREATE TABLE roles(
id int,
criteria VARCHAR (255)
);
INSERT INTO roles (id, criteria) VALUES (1, 'test AND "test"');
CREATE OR REPLACE FUNCTION test_quote()
RETURNS VARCHAR
LANGUAGE plpgsql
AS $$
DECLARE
sample varchar;
rec record;
BEGIN
FOR rec IN
SELECT * FROM roles
LOOP
sample := rec.criteria;
END LOOP;
RETURN sample;
END
$$;
SELECT * FROM test_quote();
Thx guys!

How to nest variable in query string passed to function

I need to be able to get the value stored inside rec_key.empname when I call this function:
CREATE OR REPLACE FUNCTION public.txt(text)
RETURNS SETOF record
LANGUAGE plpgsql
AS $function$
declare
var_param text;
var_req TEXT;
rec_key record;
cur_key CURSOR FOR Select empname::varchar from employee;
BEGIN
open cur_key;
loop
fetch cur_key into rec_key;
EXIT WHEN NOT FOUND;
var_req :=
'
' || $1 || '
';
return query execute var_req;
end loop;
close cur_key;
END
$function$
;
What do I have to change to get the desired empname when calling the function?
If I call it like this it doesn't work: :(
select * from public.txt('select empid, age::integer,''''''|rec_key.empname|''''''::varchar from employee') as (empid integer, age integer, empname varchar)
To address the question asked:
CREATE OR REPLACE FUNCTION public.txt(_sql text)
RETURNS SETOF record
LANGUAGE plpgsql AS
$func$
DECLARE
_rec record;
BEGIN
FOR _rec IN
SELECT empname::text FROM employee
LOOP
RETURN QUERY EXECUTE _sql
USING _rec.empname;
END LOOP;
END
$func$;
Call:
SELECT * FROM public.txt('SELECT empid, age::integer, $1 AS empname FROM employee')
AS (empid integer, age integer, empname varchar);
The example does not make any sense, though, and all of it could be replaced with a simple query. See my anser to your earlier question:
Doesn't find variable when passing query as parameter
Use the much simpler implicit cursor of a FOR loop. See:
Cursor based records in PostgreSQL
Truncating all tables in a Postgres database
Pass the variable as value with a USING clause. $1 is the symbol to reference the first USING argument. See:
Replace double quotes with single quotes in Postgres (plpgsql)

Postgresql query across different tables with dynamic query

I'm trying to get a customer id which can be placed in one of ten different tables. I don't want to hard code those table names to find it so I tried postgresql function as follows.
create or replace FUNCTION test() RETURNS SETOF RECORD AS $$
DECLARE
rec record;
BEGIN
select id from schema.table_0201_0228 limit 1 into rec;
return next rec;
select id from schema.table_0301_0331 limit 1 into rec;
return next rec;
END $$ language plpgsql;
select * from test() as (id int)
As I'm not familiar with postgresql function usage, how can I improve the code to replace 'schema.table1' with a variable, loop each table and return the result?
NOTE: table names may change overtime. For example, table_0201_0228 and table_0301_0331 are for February and March respectively.
You need dynamic SQL for that:
create or replace FUNCTION test(p_schema text)
RETURNS table(id int)
AS $$
DECLARE
l_tab record;
l_sql text;
BEGIN
for l_tab in (select schemaname, tablename
from pg_tables
where schemaname = p_schema)
loop
l_sql := format('select id from %I.%I limit 1', l_tab.schemaname, l_tab.tablename);
return query execute l_sql;
end loop;
END $$
language plpgsql;
I made the schema name a parameter, but of course you can hard-code it. As the function is defined as returns table there is no need to specify the column name when using it:
select *
from test('some_schema');

create child table function using cursor in postgresql

CREATE FUNCTION create_child1()
RETURNS TABLE(sys_user_id integer,
sys_service_id integer
)
LANGUAGE 'plpgsql'
COST 100
VOLATILE
ROWS 1000
AS $BODY$
DECLARE
curr_id CURSOR IS
SELECT id FROM users WHERE id in (3089,3090,3091,3092);
v_id bigint;
BEGIN
OPEN curr_id;
LOOP
FETCH curr_id INTO v_id;
EXIT WHEN not found ;
EXECUTE format('
CREATE TABLE IF NOT EXISTS %I (
sys_user_id integer,
sys_service_id integer
id bigint NOT NULL primary key
)
INHERITS (telemetry_master)
WITH (
OIDS=FALSE
)', 'telemetry_' || v_id);
end loop;
close curr_id;
fetch next from curr_id into v_id;
END
$BODY$ LANGUAGE plpgsql;
You do not need an explicit cursor in your function. You can use a simple FOR ... IN ... LOOP.
It is unclear what you want to return from the function. For example, it can return a readable text about each created table.
CREATE OR REPLACE FUNCTION create_child1()
RETURNS SETOF text LANGUAGE plpgsql
AS $BODY$
DECLARE
v_id int;
BEGIN
FOR v_id IN 3089..3092 LOOP
EXECUTE format('
CREATE TABLE IF NOT EXISTS telemetry_%s (
sys_user_id integer,
sys_service_id integer,
id bigint NOT NULL primary key
)
INHERITS (telemetry_master)', v_id);
RETURN NEXT format('telemetry_%s created.', v_id);
END LOOP;
END $BODY$;
Use:
SELECT create_child1();
create_child1
-------------------------
telemetry_3089 created.
telemetry_3090 created.
telemetry_3091 created.
telemetry_3092 created.
(4 rows)
If the ids are not consecutive you can use unnest(), e.g.:
FOR v_id IN SELECT id FROM unnest(array[3000,3001,3020,3021]) AS id LOOP

Aftre update deletion of row

I have the following table called 'second'
TABLE public.second
(
userid bigint,
companyid bigint
)
and table called visibility_matrix
CREATE TABLE public.visibility_matrix
(
name character varying,
companyid bigint
)
I created a trigger after I update the 'second' table I want to delete the row in visibility matrix ,I tried to achieve it via deleting the row with the companyid in visibility_matrix but it didn't work,any suggestions? here is the trigger:
CREATE OR REPLACE FUNCTION pos_org_rel_refresh()
RETURNS trigger AS
$$
DECLARE
r Integer ;
BEGIN
IF TG_OP='UPDATE' THEN
DELETE FROM visibility_matrix where companyid=NEW.companyid;
END IF;
END;
$$
LANGUAGE 'plpgsql';
CREATE TRIGGER test_trigger
AFTER UPDATE OR DELETE
ON second
FOR EACH ROW
EXECUTE PROCEDURE pos_org_rel_refresh();
you are missing a return statement, this should fix it:
CREATE OR REPLACE FUNCTION pos_org_rel_refresh() RETURNS trigger AS
$$
DECLARE
r Integer ;
BEGIN
IF TG_OP='UPDATE' THEN
DELETE FROM visibility_matrix where companyid=NEW.companyid;
RETURN NEW;
END IF;
RETURN NULL;
END;
$$
LANGUAGE 'plpgsql';