2016-12-07 33 views
0

記錄鑑於有數量和類型的表:甲骨文:創建功能更新基於參數

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

我想寫的輸入參數(量)的函數,其更新pay_type從「d」到總和等於傳遞參數的行的'A'。

例如,如果我通過2.50發揮作用,其結果應該是這樣的:

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

另外的問題是,如果我傳遞給函數2.40比功能應該分爲兩個部分,只分裂一行之後更新表。

+0

你怎麼十二月哪些行應該用於「第一」?在你的例子中,你最後留下了0.10行 - 你按照它們的數量降序來使用行嗎?這些行也有其他值(表中的其他值),您可以通過它們區分它們嗎?如果傳入的變量超過表中所有值的總和,結果應該是多少? – mathguy

+0

@mathguy傳遞的值永遠不會超過總和。 – elene

+0

Ans也可以首先更新哪個行並不重要,目標是更新總和等於傳遞值的所有行。 – elene

回答

0

這是MERGE聲明的強大功能的絕佳例證。我們需要更新一些行,可能會刪除一行(如果總量不是所選行的確切總和,則必須拆分該行)並插入兩行(再次替換「分割」行)。

按照我所做的方式書寫聲明需要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