How to return number of iterations in for loop? PostgreSQL - postgresql

I'm trying to return the number of times a for loop in postgreSQL has iterated.
table:
players(season_year,firstname, lastname, season_win, season_loss, playoff_win, playoff_loss)
What I'm attempting to do is use an aggregate to see which player has the most overall season wins SUM(p.season_win + p.playoff_win - p.season_loss - p.playoff_loss). Using this aggregate and the season year, I want to return what rank player with firstname = 'x' lastname = 'y' is in the win/loss differential. However, I cannot seem to understand how the for loop works.
Here is what I've attempted with what I've looked up online:
CREATE OR REPLACE FUNCTION get_player_rank(year INTEGER, firstn VARCHAR, lastn VARCHAR) RETURNS INTEGER AS $$
DECLARE player_rank INTEGER;
BEGIN
FOR player_rank IN
SELECT p.season_year, p.lastname, p.firstname, SUM(season_win + playoff_win - season_loss - playoff_loss) as total_wins
FROM player p
GROUP BY p.year, p.lastname, p.firstname
WHERE p.season_year = year LOOP
IF(firstn = p.firstname AND lastn = p.lastname) THEN
RETURN player_rank;
END IF
END LOOP
END
$$ LANGUAGE plpgsql;
I really appreciate any help I can get!
Thanks!

Try something like this:
-- ...
RETURN NEXT player_rank; -- return current row of SELECT
END IF
END LOOP
-- ...
See this for more info on return: http://www.postgresql.org/docs/9.1/static/plpgsql-control-structures.html

Related

how to get the output for specific column in this function from postgresql?

what is the mistake I am doing here? I have tried to call the function like this, but getting 0 rows. please, anyone, suggest me an idea to get the output. Thanks in advance
input:
if input is given as select * from test2('township'),i have to get output as
|township|--column name
|--------|
|1|
|3|
|7|
|9|
if input given as select * from test2('range'),output will be
|range|--column name
|-----|
|4|
|8|
|35|
|21|
below one is the code:
create type holder as (t text);
CREATE OR REPLACE FUNCTION test2(text) returns setof holder as
'
declare
township holder%rowtype;
range holder%rowtype;
begin
if township then
for township in
SELECT distinct "Township" as township
from tableA
loop
return next township;
end LOOP;
return;
end if;
if range then
for range in
SELECT distinct "Range" as range
from tableB
loop
return next range;
end LOOP;
return;
end if;
end
'
language 'plpgsql';```

Include IF.. ELSE condition in plpgsql and generating dynamic query

Hi all i have 2 tables dcrhd ( which holds current data) and dcrhd_arc(which holds historical data) and
I have created a function to get some data from theses tables.But this function satisfies only half of my requirement(it checking data from dcrhd table only) i will share my function here..
CREATE OR REPLACE FUNCTION dcr_report( --fin_year_flag,
finid integer, prdid integer, comp_cd CHARACTER varying, divid integer, fsid integer) RETURNS refcursor LANGUAGE 'plpgsql' AS $BODY$
DECLARE
ref refcursor;
BEGIN
open ref for SELECT hd.report_no,
hd.dcr_date,
coalesce(pr2.para_descr,' ') work_type,
coalesce(pr1.para_descr,' ') hq_type,
coalesce(rm.route_name,' ') route_name,
coalesce(hd.doctor_visits,0) doctor_visits,
coalesce(hd.stockist_visits,0) stockist_visits,
coalesce(hd.retailer_visits,0) retailer_visits,
hd.dcr_id,
fm.fs_name,
hd.fstaff_id,
CASE hd.status
WHEN 'A' THEN 'APPROVED'
WHEN 'D' THEN 'DISCARDED'
WHEN 'F' THEN 'FORWARDED'
WHEN 'E' THEN 'DRAFT'
END
status,
zsm.fs_name report1,
rsm.fs_name report2,
fm.geog_lvl1_hq,
fm.level_code,
coalesce(pm.para_descr,'SELF') joint_work,
fm.fs_code,
fm.emp_code,
coalesce(hd.doc_other,0) doc_other
FROM dcrhd hd
LEFT OUTER JOIN parameters pm ON hd.jfw = pm.para_code AND pm.para_type = 'JFW'
LEFT OUTER JOIN route_master rm ON rm.fstaff_id = hd.fstaff_id AND rm.route_id = hd.route_id AND rm.company_cd
= comp_cd
LEFT OUTER JOIN parameters pr1 ON pr1.para_code = hd.hq_exhq AND pr1.para_type = 'HQ_',
parameters pr2,
field_master fm,
field_master zsm,
field_master rsm
WHERE hd.period_id = prdid AND hd.fin_year_id = finid AND hd.fstaff_id = fm.fs_id AND fm.mgr_level4 =
zsm.fs_id AND fm.mgr_level3 = rsm.fs_id AND fm.fs_id =
CASE
WHEN fsid = 0 THEN fm.fs_id
ELSE fsid
END
AND fm.div_id =
CASE
WHEN divid = 0 THEN fm.div_id
ELSE divid
END
AND fm.fs_id = hd.fstaff_id AND fm.level_code = '005' AND pr2.para_code = hd.work_type AND pr2.
para_type = 'WTP' AND hd.company = comp_cd AND fm.company_cd = comp_cd
ORDER BY fm.fs_name,
dcr_date;
RETURN REF;
END;
$BODY$;
My requirement is I just want to add a new parameter called 'fin_year_flag'
and select the master table accordingly (like , if fin_year_flag='current'
then go to dcrhd else goto dcrhd_arc can I achive this???
Would you guys please share your ideas on this??? and is there any other way to full fill my requirement??I am new to PostgreSQL googled many times on internet but couldn't find anything helpful..
The code you have posted is huge, so let me demonstrate you how to use a table name dynamically and return a CURSOR for it by simplifying it.
I create the two tables with a sample row.
create table dcrhd as select 'CURRENT' ::TEXT as col;
create table dcrhd_arc as select 'ARCHIVED'::TEXT as col;
This is a function which uses OPEN <refcursor> FOR EXECUTE over the dynamically generated query. You need to escape the single quotes in your main SQL using another quote or use dollar quoting.
The table name is set using fin_year_flag from a CASE expression.
CREATE OR REPLACE FUNCTION dcr_report( fin_year_flag TEXT)
RETURNS refcursor LANGUAGE plpgsql
AS $BODY$
DECLARE
ref refcursor;
v_table_name TEXT := CASE fin_year_flag
WHEN 'current' THEN 'dcrhd'
ELSE 'dcrhd_arc' END;
v_sql text := format('select col from %s',v_table_name );
BEGIN
open ref for EXECUTE v_sql ;
RETURN REF;
END;
$BODY$;

Update table every 1000 rows

I am trying to do an update on a specific record every 1000 rows using Postgres. I am looking for a better way to do that. My function is described below:
CREATE OR REPLACE FUNCTION update_row()
RETURNS void AS
$BODY$
declare
myUID integer;
nRow integer;
maxUid integer;
BEGIN
nRow:=1000;
select max(uid_atm_inp) from tab into maxUid where field1 = '1240200';
loop
if (nRow > 1000 and nRow < maxUid) then
select uid from tab into myUID where field1 = '1240200' and uid >= nRow limit 1;
update tab
set field = 'xxx'
where field1 = '1240200' and uid = myUID;
nRow:=nRow+1000;
end if;
end loop;
END; $BODY$
LANGUAGE plpgsql VOLATILE
How can I improve this procedure? I think there is something wrong. The loop does not end and takes too much time.
To perform this task in SQL, you could use the row_number window function and update only those rows where the number is divisible by 1000.
Your loop doesn't finish because there is no EXIT or RETURN in it.
I doubt you could ever rival the performance of a standard SQL update with a procedural loop. Instead of doing it a row at a time, just do it all as a single statement:
with t2 as (
select
uid, row_number() over (order by 1) as rn
from tab
where field1 = '1240200'
)
update tab t1
set field = 'xxx'
from t2
where
t1.uid = t2.uid and
mod (t2.rn, 1000) = 0
Per my comment, I am presupposing what you mean by "every 1000th row," as without some designation of how to determine what tuple is what row number. That is easily edited by changing the "order by" criteria.
Adding a second where clause on the update (t1.field1 = '1240200') can't hurt but might not be necessary if these are nested loop.
This might be notionally similar to what Laurenz has in mind.
I solved this way:
declare
myUID integer;
nRow integer;
rowNum integer;
checkrow integer;
myString varchar(272);
cur_check_row cursor for select uid , row_number() over (order by 1) as rn, substr(fieldxx,1,244)
from table where field1 = '1240200' and uid >= 1000 ORDER BY uid;
BEGIN
open cur_check_row;
loop
fetch cur_check_row into myUID, rowNum, myString;
EXIT WHEN NOT FOUND;
select mod(rowNum, 1000) into checkrow;
if checkrow = 0 then
update table
set fieldxx= myString||'O'
where uid in (myUID);
end if;
end loop;
close cur_check_row;

Recursive with cursor on psql, nothing data found

How to use a recursive query and then using cursor to update multiple rows in postgresql. I try to return data but no data is found. Any alternative to using recursive query and cursor, or maybe better code please help me.
drop function proses_stock_invoice(varchar, varchar, character varying);
create or replace function proses_stock_invoice
(p_medical_cd varchar,p_post_cd varchar, p_pstruserid character varying)
returns void
language plpgsql
as $function$
declare
cursor_data refcursor;
cursor_proses refcursor;
v_medicalCd varchar(20);
v_itemCd varchar(20);
v_quantity numeric(10);
begin
open cursor_data for
with recursive hasil(idnya, level, pasien_cd, id_root) as (
select medical_cd, 1, pasien_cd, medical_root_cd
from trx_medical
where medical_cd = p_pstruserid
union all
select A.medical_cd, level + 1, A.pasien_cd, A.medical_root_cd
from trx_medical A, hasil B
where A.medical_root_cd = B.idnya
)
select idnya from hasil where level >=1;
fetch next from cursor_data into v_medicalCd;
return v_medicalCd;
while (found)
loop
open cursor_proses for
select B.item_cd, B.quantity from trx_medical_resep A
join trx_resep_data B on A.medical_resep_seqno = B.medical_resep_seqno
where A.medical_cd = v_medicalCd and B.resep_tp = 'RESEP_TP_1';
fetch next from cursor_proses into v_itemCd, v_quantity;
while (found)
loop
update inv_pos_item
set quantity = quantity - v_quantity, modi_id = p_pstruserid, modi_id = now()
where item_cd = v_itemCd and pos_cd = p_post_cd;
end loop;
close cursor_proses;
end loop;
close cursor_data;
end
$function$;
but nothing data found?
You have a function with return void so it will never return any data to you. Still you have the statement return v_medicalCd after fetching the first record from the first cursor, so the function will return from that point and never reach the lines below.
When analyzing your function you have (1) a cursor that yields a number of idnya values from table trx_medical, which is input for (2) a cursor that yields a number of v_itemCd, v_quantity from tables trx_medical_resep, trx_resep_data for each idnya, which is then used to (3) update some rows in table inv_pos_item. You do not need cursors to do that and it is, in fact, extremely inefficient. Instead, turn the whole thing into a single update statement.
I am assuming here that you want to update an inventory of medicines by subtracting the medicines prescribed to patients from the stock in the inventory. This means that you will have to sum up prescribed amounts by type of medicine. That should look like this (note the comments):
CREATE FUNCTION proses_stock_invoice
-- VVV parameter not used
(p_medical_cd varchar, p_post_cd varchar, p_pstruserid varchar)
RETURNS void AS $function$
UPDATE inv_pos_item -- VVV column repeated VVV
SET quantity = quantity - prescribed.quantity, modi_id = p_pstruserid, modi_id = now()
FROM (
WITH RECURSIVE hasil(idnya, level, pasien_cd, id_root) AS (
SELECT medical_cd, 1, pasien_cd, medical_root_cd
FROM trx_medical
WHERE medical_cd = p_pstruserid
UNION ALL
SELECT A.medical_cd, level + 1, A.pasien_cd, A.medical_root_cd
FROM trx_medical A, hasil B
WHERE A.medical_root_cd = B.idnya
)
SELECT B.item_cd, sum(B.quantity) AS quantity
FROM trx_medical_resep A
JOIN trx_resep_data B USING (medical_resep_seqno)
JOIN hasil ON A.medical_cd = hasil.idnya
WHERE B.resep_tp = 'RESEP_TP_1'
--AND hacil.level >= 1 Useless because level is always >= 1
GROUP BY 1
) prescribed
WHERE item_cd = prescribed.item_cd
AND pos_cd = p_post_cd;
$function$ LANGUAGE sql STRICT;
Important
As with all UPDATE statements, test this code before you run the function. You can do that by running the prescribed sub-query separately as a stand-alone query to ensure that it does the right thing.

PostgreSQL if else syntax error

i try a query that runs on mssql however does not run postgreSQL...
SQL Query is..
IF EXISTS (SELECT * FROM Kategoriler WHERE KategoriId = 119)
BEGIN
SELECT * FROM Kategoriler
END
ELSE
SELECT * FROM Adminler
i searched it and i found in stackoverflow
DO
$BODY$
BEGIN
IF EXISTS (SELECT 1 FROM orders) THEN
DELETE from orders;
ELSE
INSERT INTO orders VALUES (1,2,3);
END IF;
END;
$BODY$
but i do not want to use DO or, $body etc... I do not want to write any function or other etc...
i want to write only if else statement in postgreSQL... Please help me...
T-SQL supports some procedural statement like IF. PostgreSQL doesn't support it, so you cannot rewrite your query to postgres simply. Sometime you can use Igor's solution, sometime you can use plpgsql (functions) and sometime you have to modify your application and move procedural code from server to client.
Try something like
SELECT *
FROM Kategoriler
UNION ALL
SELECT *
FROM Adminler
WHERE NOT EXIST (SELECT * FROM Kategoriler WHERE KategoriId = 119)
Will only work if Kategoriler and Adminler have same structure. Otherwise you need to specify list of fields instead of *
In my case I needed to know if a record existed.
I had to write a function
CREATE OR REPLACE FUNCTION public.pro_device_exists(vdn character varying)
RETURNS boolean
LANGUAGE plpgsql
AS $function$
BEGIN
IF EXISTS (SELECT 1 FROM tags WHERE device_name = upper(vdn)) THEN
return true;
ELSE
return false;
END IF;
END; $function$
Then I was able to call this function in my code ... just a portion of my code
if pro_device_exists(vdn) then
update tags
set device_id = 11 where device_id = pro_device_id(vdn) and tag_type=10;
update tags
set device_id = pro_device_id(vdn) where tag_id = vtag_id;
vmsg = (select 'Device Now set to ' || first_name || ' ' || last_name from tags where tag_id=vtag_id);
vaction = 'Refresh Device Data';
else
vmsg = 'Device is not registered on this system';
vaction = 'No Nothing';
end if;