2016-12-07 2 views
0

eine Tabelle mit Menge und Art Gegeben:Oracle: Erstellen Funktion Datensätze auf Parameter basierend zu aktualisieren

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

Ich versuche, eine Funktion mit dem Eingangsparameter (Menge), die pay_type von ‚D‘ aktualisiert zu schreiben 'A' für die Zeilen mit der Summe, die dem übergebenen Parameter entspricht.

Zum Beispiel, wenn ich 2,50 Funktion übergeben, sollte das Ergebnis wie folgt aussehen:

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

Auch das Problem ist, wenn ich auf die Funktion 2.40 als Funktion übergeben sollte eine Zeile in zwei Teile geteilt und nur nach dieser Aktualisierungstabelle.

+0

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

+0

@mathguy der übergebene Wert wird nie mehr als die Summe sein. – elene

+0

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

Antwort

0

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 
Verwandte Themen