Dies ist eine ausgezeichnete Illustration der Macht der MERGE
Aussage. Wir müssen einige Zeilen aktualisieren, möglicherweise eine Zeile löschen (die Zeile, die geteilt werden muss, wenn der Gesamtbetrag nicht die exakte Summe der ausgewählten Zeilen ist) und zwei Zeilen einfügen (um die "geteilte" Zeile zu ersetzen).
Das Schreiben der Anweisung, wie ich tat Oracle 11.2 seit ich eine WITH
Klausel mit Spaltennamen in der Erklärung verwendet; Für ältere Versionen von Oracle kann dies jedoch problemlos neu angeordnet werden (über eine Inline-Ansicht).
Die Lösung erfordert keine Funktion oder PL/SQL jeglicher Art (obwohl sie natürlich bei Bedarf in eine Funktion eingeschlossen werden kann). Es ist alles reine SQL. In meiner Lösung habe ich rowid
als Ersatz für einen richtigen Primärschlüssel verwendet. Es wäre VIEL besser, wenn die Basistabelle eine PK hätte, die anstelle von rowid
verwendet werden könnte.
Ich habe nicht gezeigt, wie die Bind-Variable einen Wert gegeben wird. In meinem Fall habe ich das in SQL Developer ausgeführt; Wann immer Sie versuchen, eine Anweisung mit Bind-Variablen in SQL Developer auszuführen, wird ein Fenster für die Eingabe geöffnet. (Ich glaube, Toad ist ähnlich.) Jedes Front-End-Programm wird seinen eigenen Mechanismus für die Zuweisung von Werten zur Bindung von Variablen haben.
Vorbereitung
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
Anweisung
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.
Outcome
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
How do you Dezember Welche Zeilen sollten "first" verwendet werden? In Ihrem Beispiel haben Sie die 0.10 Zeile als letzte verlassen - verwenden Sie die Zeilen in absteigender Reihenfolge ihrer Menge? Haben die Zeilen auch andere Werte (andere Spalten in der Tabelle), mit denen Sie sie voneinander unterscheiden können? Und was sollte das Ergebnis sein, wenn die übergebene Variable mehr als die Summe aller Werte in der Tabelle ist? – mathguy
@mathguy der übergebene Wert wird nie mehr als die Summe sein. – elene
Ans ist es auch egal, welche Zeile zuerst aktualisiert wird, das Ziel besteht darin, alle Zeilen mit der Summe zu aktualisieren, die dem übergebenen Wert entspricht. – elene