【问题标题】:How to use a custom function as column default in a PostgreSQL table如何在 PostgreSQL 表中使用自定义函数作为列默认值
【发布时间】:2020-11-07 17:31:37
【问题描述】:

我创建了一个名为 Loan 的表,其中包含以下列:

loanID SERIAL, 
annualInterestRate INT, 
numberOfYears INT, 
loanAmount NUMERIC, 
monthlyPayment NUMERIC

monthlyPayment 的计算取决于numberOfYearsloanAmountannualInterestRate,按照以下公式:

monthlyPayment = (loanAmount * monthlyInterestRate) /
                (1 - (1/Math.pow(1 + monthlyInterestRate , numberOfYears * 12) ));

我创建了一个名为get_monthly_payment() 的函数,它可以毫无问题地返回monthlyPayment。对于每一行,我想将此函数的返回设为monthlyPayment 列的默认值。 我怎样才能做到这一点?

【问题讨论】:

  • 您希望返回值是默认(允许手动更改)还是生成的列(不允许手动更改)。
  • monthlyInterestRateMath.pow 定义在哪里?
  • 无手动更改。 Math.pow 我的意思是幂函数,它s just a java function. monthlyInterestRate`是一个自定义函数。 @ErwinBrandstetter
  • 好吧,除非你使用的 pl/java 是行不通的。

标签: database postgresql


【解决方案1】:

月利率可能不是您表格中的一列,但年利率是。但每年到每月是一个简单的计算。以下内容可用于 v12 生成的列和使用触发器的先前版本。此外,它还提供了一个通常有用的 Postgres 函数,用于计算每月付款。请参阅fiddle

create or replace 
function monthly_loan_payment 
       ( amount  numeric
       , apr     numeric
       , term    numeric
       )
  returns numeric
  language sql 
  immutable strict  
/*  Given a loan amount, the Annual Percent (Interest) Rate and term (in years) 
 *  compute the monthly payment to service the loan. 
 *  Note. Monthly payment calculates correctly, but due to the exact terms of loan
 *        and date of payment receipt adjustments for end of loan payment may be 
 *        required.  
 */ 
as $$   
  with monthly (mrate) as (values ( (apr/100.00) / 12.00 ) )
  select round((amount * mrate)  /(1.0 - (1.0/ ((1.0 +mrate)^( term * 12.00)) ))::numeric,2) 
     from monthly;
$$; 

【讨论】:

    【解决方案2】:

    如果monthlyPayment 根据您的定义是固定的,因此完全依赖于功能,那么请考虑完全不保留该值。继续使用您的廉价(!)功能。整体上更清洁,更便宜。喜欢:

    SELECT *, f_monthly_payment(l) AS monthly_payment
    FROM loan l;
    

    假设函数定义为f_monthly_payment(loan)(以表的行类型为参数)。否则相应地进行调整。

    Postgres 12 或更高版本具有 STORED 生成的列,但这些只为昂贵的计算付费。持久化列占用空间并减慢表上的所有操作。

    见:

    如果您想允许手动更改,默认列是可行的方法(就像您实际问过的一样)。但是你不能使用你的函数,因为quoting the manual:

    DEFAULT 子句为其指定的列分配默认数据值 它出现在列定义中。该值是任何无变量的 表达式(特别是对其他列的交叉引用) 不允许当前表)。

    剩下的解决方案是在桌子上触发BEFORE INSERT,例如:

    CREATE OR REPLACE FUNCTION trg_loan_default_rate()
      RETURNS trigger
      LANGUAGE plpgsql AS
    $func$
    BEGIN
       NEW.monthlyPayment := (NEW.loanAmount * monthlyInterestRate())
                        / (1 - (1/power(1 + monthlyInterestRate(), NEW.numberOfYears * 12)));
    
       RETURN NEW;
    END
    $func$;
    
    CREATE TRIGGER loan_default_rate
    BEFORE INSERT ON loan
    FOR EACH ROW EXECUTE FUNCTION trg_loan_default_rate();
    

    假设monthlyInterestRate() 是一个自定义函数。
    我用内置的Postgres function power()替换了Math.pow

    NEW是触发函数中的特殊记录变量,指的是新插入的行。见:

    EXECUTE FUNCTION 需要 Postgres 11。请参阅:

    相关:

    另外:考虑 Postgres 中合法的、小写的、不带引号的标识符。见:

    【讨论】:

    • 从您的代码看来,我们在创建触发器时没有将NEW 运算符放在自定义函数之前。只需将它放在列的属性之前。那样可以么?。另一个问题是:如果monthlyInterestRate()有参数,我们是不是也要在参数前加上NEW操作符?
    • @HusseinEid: NEW 是触发器函数中的一个特殊记录变量,指的是新插入的行。我在上面添加了一个链接。
    【解决方案3】:

    在 Postgres 12+ 中,您已经生成了列:

    生成的列是始终从其他列计算的特殊列。

    “生成的列是一个特殊的列,它总是从其他列中计算出来的。...”

    所以:

    monthlyPayment numeric GENERATED ALWAYS AS(loanAmount * monthlyInterestRate) /
                    (1 - (1/Math.pow(1 + monthlyInterestRate , numberOfYears * 12) ))  STORED
    

    在版本 12 之前,您必须使用 CREATE TRIGGER 向表中添加一个触发器,该触发器调用包含上述公式的函数并设置列值。

    【讨论】:

    • 这太好了。但是monthlyInterestRate不是表中的列,是自定义函数。我们可以在GENERATED ALWAYS 子句中使用custome 函数吗?。
    • 生成的列在原则上与具有触发器生成的默认值的列不同。后者是一个允许手动更改的普通列。生成的列是不可能的。见:stackoverflow.com/a/8250729/939860
    • @Hussein Eid 如果monthlyInterestRate 是一个函数,我看不到函数调用。对我来说,它看起来像一个变量/列名。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-21
    • 2013-12-30
    • 1970-01-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 2011-08-01
    相关资源
    最近更新 更多