创建一个接受字符串和 returns 多行的函数

Create a function that accepts a string and returns multiple rows

我需要创建一个函数,根据用户的输入转换单个列的值。为此,我需要一些语法方面的帮助。

这是我当前为获取行而执行的查询:

SELECT payment_id, rental_id, amount FROM payment

关于我正在尝试做的事情的一些伪代码:

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

    }
}

我一直在尝试创建一个,但我在语法上遇到困难。
不工作:

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;

有人可以帮我了解创建此函数的语法吗?

像这样

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

如果您使用

,它会以 table 的形式 return
select * from get_data('EUR');

这里有一个演示

demo in db<>fiddle

Postgres 14 或更高版本

在 Postgres 14 或更高版本中,我建议使用新的标准 SQL 语法:

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;

参见:

以下大部分内容仍然适用...

Postgres 13(或任何版本)

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

坚持使用 LANGUAGE sql(就像您最初的尝试一样)。简单函数不需要 LANGUAGE plpgsql - 除非您想为无效输入添加自定义错误消息或错误处理..

参见:

  • Difference between language sql and language plpgsql in PostgreSQL functions

使用更简单的 switched CASE(就像您最初的尝试一样)。

  • Display column name with max value between several columns
  • Simplify nested case when statement

提供明确的 ELSE 分支。 (如果未拼写 ELSESQL CASE 默认为 NULL。)

如果 payment_info,在您最初的尝试中,是与您想要的 return 类型匹配的现有行类型,请使用简单的 RETURNS SETOF payment_info 而不是 RETURNS TABLE(...).

table-限定列 可能具有不明确的名称 是一种很好的风格,就像演示的那样。 (在任何情况下,从来都不是一个坏主意。)
但它是 LANGUAGE plpgsql 函数中的 要求 ,其中 RETURNS TABLE ...) 隐式声明与 table 列同名的 OUT 参数。这会引发异常,例如:

ERROR: column reference "payment_id" is ambiguous

参见:

  • How to return result of a SELECT inside a function in PostgreSQL?

另外:numeric(5,2)? 这引发了 amount > 999.99 的异常。看起来像一把上了膛的步兵枪。只需使用 numeric 或类似 numeric(20,2) 的舍入效果即可。

关于STABLE:

关于PARALLEL SAFE:

最后,已经清理完毕,分解结果行,调用SELECT * FROM:

SELECT * FROM get_data('USD');

参见:

  • Simple PostgreSQL function to return rows
  • PostgreSQL: ERROR: 42601: a column definition list is required for functions returning "record"

我为输入参数添加了一个默认值DEFAULT 'USD'。这很方便,但完全是可选的。它记录了最常见的预期,并允许在没有显式参数的情况下进行简短调用:

SELECT * FROM get_data();