74

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?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Noob Doob
  • 1,757
  • 3
  • 19
  • 27
  • 2
    The right way to do it would be: "dont't iterate". What you seem to do is perfectly feasable using plain SQL : `insert into leaderboards(a,b,c) select x,y,z from users;` – wildplasser Sep 19 '15 at 12:40
  • As I noticed in the answer, `old_seasonnum` is not selected at this point from `user_data.users`, but in a previous point. – Noob Doob Sep 19 '15 at 13:14
  • It is allowed to use a constant (or a variable, in plpgsql or in prepared statements) where an expression is needed: `insert into foo(a,b,c) select 42, y, z from bar;` Note: in your code `old_seasonnum` is not even defined. – wildplasser Sep 19 '15 at 13:22
  • 1
    I just put here the part of the code I had the problem at, it is defined in previous sectors in the actual code. If you feel it's necessary, I will put the whole code here. – Noob Doob Sep 19 '15 at 14:30

4 Answers4

122

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
Renzo
  • 26,848
  • 5
  • 49
  • 61
15

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; $$
moffeltje
  • 4,521
  • 4
  • 33
  • 57
Tzahi Leh
  • 2,002
  • 1
  • 15
  • 27
14

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
$$;
Thushara Buddhika
  • 1,652
  • 12
  • 14
0

@Tazahi Leh perfect - thank you! But don't forget a semicolon after END :)

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;
$$
philgsk
  • 51
  • 9