這是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
你怎麼十二月哪些行應該用於「第一」?在你的例子中,你最後留下了0.10行 - 你按照它們的數量降序來使用行嗎?這些行也有其他值(表中的其他值),您可以通過它們區分它們嗎?如果傳入的變量超過表中所有值的總和,結果應該是多少? – mathguy
@mathguy傳遞的值永遠不會超過總和。 – elene
Ans也可以首先更新哪個行並不重要,目標是更新總和等於傳遞值的所有行。 – elene