create child table function using cursor in postgresql - 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

Related

How to upsert with serial primary key

I have a stored procedure to perform an upsert. However the conflict condition never runs, passing an existing ID always causes it to create a new record.
create or replace function upsert_email(v_id bigint, v_subject character varying)
returns bigint
language plpgsql
as $$
declare
v_id bigint;
begin
insert into emails
(id, subject)
values (coalesce(v_id, (select nextval('serial'))), v_subject)
on conflict(id)
do update set (subject) = (v_subject) where emails.id = v_id
returning id into v_id;
return v_id;
end;
$$;
When running select upsert_email(6958500, 'subject'); which is a record that exists, it always creates a new record instead.
I have already looked at: Upsert/on conflict with serial primary key which is the most similar question and is what my SQL is modeled on, however I haven't been able to get it to work.
Wow, idiot of the year award goes to me.
I'm taking a parameter called v_id, then immediately overwrite it in the declare with a v_id; They should be named 2 different things:
create or replace function upsert_email(v_id bigint, v_subject character varying)
returns bigint
language plpgsql
as $$
declare
v_return_id bigint;
begin
insert into emails
(id, subject)
values (coalesce(v_id, (select nextval('serial'))), v_subject)
on conflict(id)
do update set (subject) = (v_subject) where emails.id = v_id
returning id into v_return_id;
return v_return_id;
end;
$$;

Insert within function fails with "query has no destination for result data"

Here is the complete example code:
CREATE TABLE testtbl (
id integer NOT NULL,
intval integer,
strval varchar(64)
);
CREATE SEQUENCE testtbl_id_seq
START WITH 1 INCREMENT BY 1
NO MINVALUE NO MAXVALUE CACHE 1;
ALTER SEQUENCE testtbl_id_seq OWNED BY testtbl.id;
ALTER TABLE ONLY testtbl ALTER COLUMN id SET DEFAULT
nextval('testtbl_id_seq'::regclass);
ALTER TABLE ONLY testtbl ADD CONSTRAINT testtbl_pkey PRIMARY KEY (id);
CREATE FUNCTION insert_testtbl (p_intval integer, p_strval varchar(64))
RETURNS integer AS $$
DECLARE
v_new_id integer;
BEGIN
INSERT INTO testtbl (intval, strval) VALUES (p_intval, p_strval)
RETURNING v_new_id;
RETURN v_new_id;
END;
$$ LANGUAGE plpgsql;
SELECT insert_testtbl(1, 'One');
When I run this (PostgreSQL version is 9.6.1), I get:
ERROR: query has no destination for result data
CONTEXT: PL/pgSQL function insert_testtbl(integer,character varying) line 5 at SQL statement
This doesn't make sense; I AM specifying a destination for the result!
What am I doing wrong here? Thanks!!!
I am specifying a destination for the result!
No you are not.
RETURNING v_new_id; simply means:
"return the current value of the variable v_new_id from this insert statement"
(which is null as the variable was never assigned a value)
You are not storing the generated value anywhere.
You either need to use an into clause:
CREATE FUNCTION insert_testtbl (p_intval integer, p_strval varchar(64))
RETURNS integer AS $$
DECLARE
v_new_id integer;
BEGIN
INSERT INTO testtbl (intval, strval) VALUES (p_intval, p_strval)
RETURNING id
INTO v_new_id; --<<< HERE
RETURN v_new_id;
END;
$$ LANGUAGE plpgsql;
Or convert everything it simple SQL function:
CREATE FUNCTION insert_testtbl (p_intval integer, p_strval varchar(64))
RETURNS integer AS
$$
INSERT INTO testtbl (intval, strval) VALUES (p_intval, p_strval)
RETURNING id;
$$ LANGUAGE sql;

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';

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

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;

Postgresql : insert record using trigger

CREATE TABLE users
(
id integer NOT NULL DEFAULT nextval('userseq'::regclass)
........
)
CREATE TABLE History
(
userid integer,
createdat timestamp with time zone
)
CREATE OR REPLACE FUNCTION recordcreatetime()
RETURNS trigger AS
$BODY$
BEGIN
NEW.createdAt = NOW();
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
User and History has one-one relationship. How i can insert new record in History table before creating new user.
CREATE OR REPLACE FUNCTION recordcreatetime()
RETURNS trigger
language plpgsql
AS $$
DECLARE
BEGIN
INSERT INTO History values(new.id,NOW() );
RETURN NEW;
END;
$$;
and write trigger statement as
CREATE TRIGGER user_hist
BEFORE INSERT ON users
FOR EACH ROW EXECUTE function recordcreatetime() ;