Oracle:创建基于参数更新记录的函数

时间:2016-12-07 20:12:26

标签: sql database oracle

给出一个包含金额和类型的表:

%

我尝试编写一个带有输入参数(金额)的函数,该函数更新来自' D'到' A'对于总和等于传递参数的行。

例如,如果我将2.50传递给function,结果应如下所示:

| AMOUNT | PAY_TYPE |
| 0.50   | D        |
| 0.50   | D        |
| 0.50   | D        |
| 0.10   | D        |
| 0.50   | D        |
| 0.50   | D        |

另外问题是如果我传递给函数2.40而不是函数应该将一行分成两部分并且仅在该更新表之后。

1 个答案:

答案 0 :(得分:0)

这是MERGE陈述力量的绝佳例证。我们需要更新一些行,可能删除一行(如果总量不是所选行的确切总和,则必须拆分的行)并插入两行(再次,替换" split"行)。

按照我的方式编写语句需要Oracle 11.2,因为我使用了WITH子句,并在声明中给出了列名;但是对于旧版本的Oracle,这可以很容易地重新排列(使用内联视图)。

解决方案不需要任何类型的函数或PL / SQL(当然,如果需要,它可以包含在函数中)。它都是纯粹的SQL。

在我的解决方案中,我使用rowid代替正确的主键。如果基表具有可用于代替rowid的PK,那将会更好。

我没有说明绑定变量是如何赋值的。就我而言,我在SQL Developer中运行它;每当您尝试在SQL Developer中运行带有绑定变量的语句时,都会弹出一个输入窗口。 (我相信Toad很相似。)每个前端程序都有自己的机制来分配值来绑定变量。

<强>制备

create table inputs as
       select 0.50 amount, 'D' pay_type from dual union all
       select 0.50, 'D' from dual union all
       select 0.50, 'D' from dual union all
       select 0.10, 'D' from dual union all
       select 0.50, 'D' from dual union all
       select 0.50, 'D' from dual
;

commit;

select * from inputs; 

AMOUNT  PAY_TYPE
------  --------
   0.5  D
   0.5  D
   0.5  D
   0.1  D
   0.5  D
   0.5  D

MERGE声明

merge into inputs i
  using (
    with prep ( amount, pay_type, rn, l_amt, c_amt ) as (
           select amount, pay_type, rowid as rn,
                  coalesce(sum(amount) over (order by rowid 
                     rows between unbounded preceding and 1 preceding), 0),
                  sum(amount) over (order by rowid)
           from   inputs
         )
    select  amount, pay_type, rn, c_amt
      from  prep
      where l_amt < :input_value
    union all
      select  :input_value - l_amt, 'A', null, null
        from  prep
        where :input_value > l_amt and :input_value < c_amt
    union all
      select  c_amt - :input_value, 'D', null, null
        from  prep
        where :input_value > l_amt and :input_value < c_amt
  ) x
  on (i.rowid = x.rn)
when matched
  then update set i.pay_type = 'A'
       delete where :input_value < c_amt
when not matched
  then insert values (x.amount, x.pay_type)
;

7 rows merged.

<强>结果

select * from inputs;

AMOUNT  PAY_TYPE
------  --------
   0.5  A
   0.5  A
   0.5  A
   0.1  A
   0.5  A
   0.1  D
   0.4  A