0

I'm being required to create a function that transforms a single column's value based on the user's input. I need some help on the syntax for doing so.

Here is the query I'm currently performing to get the rows:

SELECT payment_id, rental_id, amount FROM payment

enter image description here

some pseudocode on what I'm trying to do:

function getReport(String currencyType){
    if(currencyType == 'EUR'){

       Multiply the values in the amounts column by 1.16 and append Euros to it
       Return all the rows in the table

    }else if(currencyType == 'RMB'){

       Multiple the values in the amounts column by 6.44 and append RMB to it
       Return all the rows in the table

    }else{

       Do nothing because the default column values are in USD
       Return all the rows in the table

    }
}

I've been trying to create one but I'm struggling with the syntax.
Does not work:

CREATE OR REPLACE FUNCTION get_data(currency_type text) RETURNS TABLE payment_info AS $$
    CASE currency_type
    WHEN 'EUR' THEN
        SELECT payment_id, rental_id, amount * 1.16 FROM payment;
    WHEN 'RMB' THEN
        SELECT payment_id, rental_id, amount * 6.44 FROM payment;
    WHEN 'USD' THEN
        SELECT payment_id, rental_id, amount FROM payment;
$$ LANGUAGE SQL;

Could someone please help me with the syntax to creating this function?

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
DIRTY DAVE
  • 2,523
  • 2
  • 20
  • 83
  • You would do well to read [CASE](https://www.postgresql.org/docs/current/functions-conditional.html#FUNCTIONS-CASE). Hint you can't nest the `SELECT`. – Adrian Klaver Oct 09 '21 at 20:21

2 Answers2

1

Something like this

CREATE OR REPLACE FUNCTION get_data(currency_type text) 
RETURNS TABLE  ( payment_id int, rental_id int, amount numeric(5,2) ) 
language plpgsql
as $$
begin 
   return query 
     SELECT b.payment_id, b.rental_id, 
    case 
        when currency_type = 'EUR' then b.amount * 1.16     
        when currency_type = 'RMB' then b.amount * 6.44 
        when currency_type = 'USD' then b.amount 
    end as amount 
    FROM payment b;
end;$$

It does return in the form of a table if you use

select * from get_data('EUR');

Here a demo

demo in db<>fiddle

Roberto Hernandez
  • 8,231
  • 3
  • 14
  • 43
1

Postgres 14 or later

In Postgres 14 or later, I'd suggest the new standard SQL syntax:

CREATE OR REPLACE FUNCTION get_data(_currency_type text DEFAULT 'USD')
  RETURNS TABLE (payment_id int, rental_id int, amount numeric(5,2)) 
  STABLE PARALLEL SAFE
BEGIN ATOMIC
SELECT p.payment_id, p.rental_id
     , CASE _currency_type
          WHEN 'USD' THEN p.amount
          WHEN 'EUR' THEN p.amount * 1.16
          WHEN 'RMB' THEN p.amount * 6.44
FROM   payment p;
END;

See:

Most of the below still applies ...

Postgres 13 (or any version)

CREATE OR REPLACE FUNCTION get_data(_currency_type text DEFAULT 'USD')
  RETURNS TABLE  (payment_id int, rental_id int, amount numeric(5,2)) 
  LANGUAGE sql STABLE PARALLEL SAFE AS
$func$
SELECT p.payment_id, p.rental_id
     , CASE _currency_type
          WHEN 'USD' THEN p.amount
          WHEN 'EUR' THEN p.amount * 1.16
          WHEN 'RMB' THEN p.amount * 6.44 
       -- ELSE 1/0 END   -- ??? this purposely raises an exception
FROM   payment p;
$func$;

Stick with LANGUAGE sql (like in your original attempt). There is no need for LANGUAGE plpgsql for the simple function - unless you want to add a custom error message or error handling for invalid input ..

See:

Use a simpler switched CASE (like in your original attempt).

Provide an explicit ELSE branch. (SQL CASE defaults to NULL if ELSE is not spelled out.)

If payment_info, featured in your original attempt, is an existing row type matching your desired return type, use a simple RETURNS SETOF payment_info instead of RETURNS TABLE(...).

It's good style to table-qualify columns with possibly ambiguous names like demonstrated. (It's never a bad idea in any case.)
But it's a requirement in a LANGUAGE plpgsql function with RETURNS TABLE ...) implicitly declaring OUT parameters of the same name as table columns. That would raise an exception like:

ERROR: column reference "payment_id" is ambiguous

See:

Also: numeric(5,2)? That raises an exception for amount > 999.99. Looks like a loaded foot-gun. Just use numeric or something like numeric(20,2) for the rounding effect.

About STABLE:

About PARALLEL SAFE:

Finally, as has been cleared up already, to decompose result rows, call with SELECT * FROM:

SELECT * FROM get_data('USD');

See:

I added an default value for the input parameter: DEFAULT 'USD'. That's convenient, but totally optional. It documents what's most commonly expected and allows a short call without explicit parameter:

SELECT * FROM get_data();
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228