multiple update pgsql using cursor - sql

this code not error but nothing data change please help me. I want to multiple update quantity from some stock table
drop FUNCTION SP_PROSES_STOCK(noresep bigint, p_post_cd varchar);
CREATE or replace FUNCTION SP_PROSES_STOCK(noresep bigint, p_post_cd varchar)
RETURNS void
LANGUAGE plpgsql
as $function$
DECLARE cursorData refcursor;
v_item_cd varchar;
v_quantity numeric;
begin
open cursorData FOR
select A.item_cd, A.quantity from trx_medical_resep B
inner join trx_resep_data A on A.medical_resep_seqno = B.medical_resep_seqno
where B.medical_resep_seqno = noresep;
fetch next from cursorData into v_item_cd,v_quantity;
while (found)
loop
update inv_pos_item set quantity = quantity - v_quantity
where item_cd = v_item_cd and pos_cd = p_post_cd;
end loop;
close cursorData;
END
$function$

I mopdify your function to make it shorter and use FOR. Also I add RAISE NOTICE for debugging. Can you try it:
CREATE or replace FUNCTION SP_PROSES_STOCK(noresep bigint, p_post_cd varchar)
RETURNS void
LANGUAGE plpgsql
as $function$
DECLARE v_cursor record;
BEGIN
FOR v_cursor IN
SELECT A.item_cd,
A.quantity
FROM trx_medical_resep B
JOIN trx_resep_data A ON A.medical_resep_seqno = B.medical_resep_seqno
WHERE B.medical_resep_seqno = noresep
LOOP
RAISE NOTICE 'Changes for %', v_curosr;
UPDATE inv_pos_item
SET quantity = quantity - v_cursor.quantity
WHERE item_cd = v_cursor.item_cd
AND pos_cd = p_post_cd;
END LOOP;
END
$function$
After debugging you can remove RAISE NOTICE.

You don't need a loop for this. A single UPDATE statement will be a lot faster:
CREATE or replace FUNCTION SP_PROSES_STOCK(noresep bigint, p_post_cd varchar)
RETURNS void
as
$function$
begin
update inv_pos_item
set quantity = quantity - v.quantity
from (
select A.item_cd, A.quantity
from trx_medical_resep B
join trx_resep_data A on A.medical_resep_seqno = B.medical_resep_seqno
where B.medical_resep_seqno = noresep
) v
where item_cd = v.item_cd and pos_cd = p_post_cd;
END;
$function$
LANGUAGE plpgsql;

There are scenarios where a cursor is needed. For example lets say you are updating 10 million rows and an exception is thrown at the 6th million record the the update will fail and then it will rollback all of the rows that were updated prior to the failure. If a cursor is used it will be slower but you will have greater control over the process and be able to bypass the error and continue with the posting of the updates. But then again your milage may vary...

Related

Update columns for the first user inserted

I'm trying to create a trigger and a function to update some columns (roles and is_verified) for the first user created. Here is my function :
CREATE OR REPLACE FUNCTION public.first_user()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
begin
if(select count(*) from public.user = 1) then
update new set new.is_verified = true and new.roles = ["ROLE_USER", "ROLE_ADMIN"]
end if;
return new;
end;
$function$
;
and my trigger :
create trigger first_user
before insert
on
public.user for each row execute function first_user()
I'm working on Dbeaver and Dbeaver won't persist my function because of a syntax error near the "=". Any idea ?
Quite a few things are wrong in your trigger function. Here it is revised w/o changing your business logic.
However this will affect the second user, not the first. Probably you shall compare the count to 0. Then the condition shall be if not exists (select from public.user) then
CREATE OR REPLACE FUNCTION public.first_user()
RETURNS trigger LANGUAGE plpgsql AS
$function$
begin
if ((select count(*) from public.user) = 1) then
-- probably if not exists (select from public.user) then
new.is_verified := true;
new.roles := array['ROLE_USER', 'ROLE_ADMIN'];
end if;
return new;
end;
$function$;

Can't execute or perform a funtion in postgresql

I have a syntax erro at or near ")" on executing sum function, perform doesn't work too!
That's my code:
CREATE OR REPLACE FUNCTION sum() RETURNS VOID AS $$
declare
ea bigint;
BEGIN
FOR ea in select ean from ws_products where order_code like 'BIL%'
LOOP
insert into ws_products_margins (type, amount)values ('PERSENTAGE', 30.00) returning id;
update ws_products set margin_id = id where ean = ea;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;
execute sum();
You need to return that id into the variable you have declared as rightly mentioned in the comments.
Notice the variable name has been updated, Along with the usage of a 'record' variable.
Try-
CREATE OR REPLACE FUNCTION sum() RETURNS VOID AS $$
declare
ea_id bigint;
j record;
BEGIN
FOR j in select ean from ws_products where order_code like 'BIL%'
LOOP
insert into ws_products_margins (type, amount)values ('PERCENTAGE', 30.00) returning id into ea_id;
update ws_products set margin_id = ea_id where ean = j.ean;
END LOOP;
RETURN;
END;
$$ LANGUAGE plpgsql;

Update PSQL Column with Default Date if erroneus date

I am using PostgreSQL 9.3 i have a column called appointment date in a table appointment which has erroneous dates like 21117-03-04 i wish to update all rows in the column to have a default date e.g. 1900-01-01 if the value on that column is any erroneous date. I have not yet tried any solution yet. please help.
you can use below procedure
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `MAKE_ACCURATE_DATE`()
BEGIN
DECLARE VAR_ID varchar(100);
DECLARE VAR_DATE DATE;
DECLARE VAR_FINISHED INT(11) DEFAULT 0;
DECLARE DATABASE_CURSOR CURSOR FOR
SELECT DATE(DATE_COLUMN) DATE_COLUMN,ID
FROM YOUR_TABLE_NAME;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET VAR_FINISHED = 1;
OPEN DATABASE_CURSOR;
GET_NEXTRECORD: LOOP
FETCH DATABASE_CURSOR INTO VAR_DATE,VAR_ID;
IF VAR_FINISHED = 1 THEN
LEAVE GET_NEXTRECORD;
END IF;
IF VAR_DATE =NULL THEN
UPDATE YOUR_TABLE_NAME SET DATE_COLUMN='1900-01-01' WHERE ID=VAR_ID;
END IF;
END LOOP GET_NEXTRECORD;
CLOSE DATABASE_CURSOR;
END$$
DELIMITER ;
OR
FOR POSTGRES
CREATE OR REPLACE FUNCTION IS_VALID_DATE(S VARCHAR) RETURNS BOOLEAN AS $$
BEGIN
PERFORM S::DATE;
RETURN TRUE;
EXCEPTION WHEN OTHERS THEN
RETURN FALSE;
END;
$$ LANGUAGE PLPGSQL;
And make change in above function as below.
CREATE OR REPLACE FUNCTION MAKE_ACCURATE_DATE()
RETURNS void AS
$BODY$
DECLARE
RECORD RECORD;
COUNT INT;
BEGIN
COUNT=0;
FOR RECORD IN SELECT * FROM cpad.dtl_patientappointment;
LOOP
IF(!IS_VALID_DATE(RECORD.appointmentdate)) THEN
UPDATE cpad.dtl_patientappointment SET appointmentdate='1900-01-01' WHERE patientappointmentid=RECORD.patientappointmentid;
END IF;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql;
And execute this line
SELECT MAKE_ACCURATE_DATE();
It will fetch your record from table one by one and as
DATE(DATE_COLUMN) is there,it will returns null if date is not proper.So after applying condition it will update that record with specific ID.
Hope this will helps.

How to iterate over results of query

I am creating a function in pgsql script language, and what I want to do in this point is iterate over the results of a query and for each row do something specific. My current try is the following, where temprow is declared as temprow user_data.users%rowtype. The code in question is the following:
FOR temprow IN
SELECT * FROM user_data.users ORDER BY user_seasonpts DESC LIMIT 10
LOOP
SELECT user_id,user_seasonpts INTO player_idd,season_ptss FROM temprow;
INSERT INTO user_data.leaderboards (season_num,player_id,season_pts) VALUES (old_seasonnum,player_idd,season_ptss);
END LOOP;
However I get the following error from this: ERROR: relation "temprow" does not exist. If it's clear what I want to be done, could you point to me the right way to do it?
temprow is a record variable which is bound in turn to each record of the first SELECT.
So you should write:
FOR temprow IN
SELECT * FROM user_data.users ORDER BY user_seasonpts DESC LIMIT 10
LOOP
INSERT INTO user_data.leaderboards (season_num,player_id,season_pts) VALUES (old_seasonnum,temprow.userd_id,temprow.season_ptss);
END LOOP;
This loop could be further simplified as a single query:
INSERT INTO user_data.leaderboards (season_num,player_id,season_pts)
SELECT old_seasonnum,player_idd,season_ptss FROM user_data.users ORDER BY user_seasonpts DESC LIMIT 10
A function that loop through the select and use loop item values to filter and calculate other values,
CREATE FUNCTION "UpdateTable"() RETURNS boolean
LANGUAGE plpgsql
AS
$$
DECLARE
TABLE_RECORD RECORD;
BasePrice NUMERIC;
PlatformFee NUMERIC;
MarketPrice NUMERIC;
FinalAmount NUMERIC;
BEGIN
FOR TABLE_RECORD IN SELECT * FROM "SchemaName1"."TableName1" -- can select required fields only
LOOP
SELECT "BasePrice", "PlatformFee" INTO BasePrice, PlatformFee
FROM "SchemaName2"."TableName2" WHERE "UserID" = TABLE_RECORD."UserRID";
SELECT "MarketPrice" / 100 INTO MarketPrice FROM "SchemaName3"."TableName3" WHERE "DateTime" = TABLE_RECORD."DateTime";
FinalAmount = TABLE_RECORD."Qty" * (BasePrice + PlatformFee - MarketPrice);
UPDATE "SchemaName1"."TableName1" SET "MarketPrice" = MarketPrice, "Amount" = CFDAmount
WHERE "ID" = CFD_RECORD."ID"; -- can update other schema tables also
END LOOP;
RETURN TRUE;
END
$$;
For future reference, I want to emphasise Thushara comment on the accepted answer
On Postgres#12 the following would work:
DO $$
DECLARE temprow RECORD;
BEGIN FOR temprow IN
SELECT * FROM user_data.users ORDER BY user_seasonpts DESC LIMIT 10
LOOP
INSERT INTO user_data.leaderboards (season_num,player_id,season_pts) VALUES (old_seasonnum,temprow.userd_id,temprow.season_ptss);
END LOOP;
END $$

Looping through a postgres table and returning the wekid

I have a requirement to loop through a table having all the reporting periods right from 2010.
I need to loop through this table so that i when I pass the date string, it takes at and compares it if it is less than the reporting_periods_o and returns back the reporting_period_string.
CREATE OR REPLACE FUNCTION rpt.reportingperiods(IN v_date text)
RETURNS SETOF text AS
$BODY$
DECLARE
v_in_date INT := CAST(v_date AS INT);
i INT;
v_sk INT;
BEGIN
For i IN SELECT rpt_sk FROM rpt.REPORTING_PERIODS LOOP
SELECT rpt_sk INTO v_sk FROM rpt.REPORTING_PERIODS where rpt_sk = i;
IF v_in_date <= (SELECT reporting_periods_o FROM rpt.REPORTING_PERIODS
WHERE rpt_sk = i)
THEN RETURN QUERY SELECT MIN(reporting_periods)reporting_periods FROM rpt.REPORTING_PERIODS WHERE rpt_sk = v_sk AND CAST(reporting_periods AS DATE) > CAST(now() AS DATE);
END IF;
END LOOP;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100
ROWS 1000;
My reporting periods has three fields:
rpt_sk, reporting_periods, reporting_periods_o
1 2014-03-08 20140308
I have implemented a function but when I execute I am getting multiple records, I need to be able to get back one record.