2009-03-06 8 views
0

Ich habe zwei Tabellen:Aktualisieren von Datenbanktabellen, wenn Werte fehlen

Bill:

create table Bill(
       BillDate datetime, 
       Amount decimal(10,2) , 
       BillCurrency varchar(3) , 
       ReportingCurrency decimal(10,2)) 

FxRate:

create table FxRate( 
       RateDate datetime, 
       SourceCurrency varchar(3), 
       TargetCurrency varchar(3), 
       ExchangeRate decimal(15,4)) 

Dies ist, was ich tun möchte:

Ich möchte Sie pdate meine Bill Tabelle als

update Bill 
set ReportingCurrency = FxRate.ExchangeRate * Bill.Amount 
from FxRate 
where FxRate.RateDate = Bill.BillDate 

In diesem Update werden alle Zeilen, die einen Eintrag für diesen bestimmten Zeitpunkt haben die neuen reportingcurrency Daten. Da die Bill-Tabelle mehrere Zeilen haben kann, die für die Aktualisierung infrage kommen, habe ich folgendes Problem:

Für die Zeilen, für die in der FxRate-Tabelle (für dieses Datum) kein Eintrag vorhanden war, wird die ReportingCurrency NULL. Ich möchte zurück zum nearest <= RateDate gehen und den Wechselkurs abholen. Ist das möglich mit Änderungen in der gleichen Update-Anweisung oder einer anderen effizienten Methode? (Ich möchte einen Cursor vermeiden).

+0

D'oh. Korrigiert einen dummen Fehler in meiner Antwort. – Tomalak

Antwort

0

Natürlich ist dies möglich - als SELECT dies wäre:

SELECT 
    b.BillDate, 
    r.RateDate, 
    r.ExchangeRate 
FROM 
    Bill b 
    LEFT JOIN FxRate r ON r.RateDate = (
    SELECT MAX(RateDate) FROM FxRate WHERE RateDate <= b.BillDate 
) 

Deshalb, als UPDATE:

UPDATE 
    Bill 
SET 
    ReportingCurrency = r.ExchangeRate * b.Amount 
FROM 
    Bill b 
    LEFT JOIN FxRate r ON r.RateDate = (
    SELECT MAX(RateDate) FROM FxRate WHERE RateDate <= b.BillDate 
) 

Die Lösung diese Annahmen macht:

  • dort ist nie mehr als ein FxRate Rekord für einen beliebigen Tag
  • gibt es einen FxRate Datensatz, der alle Bill Aufzeichnungen

früher Wenn diese Annahmen nicht zutreffen, muss die Abfrage untergebracht werden.

Beachten Sie auch, dass dies die SQL Server-Syntax ist. Es ist möglich, dass Sybase ein wenig anders ist.


Da ein Kommentator zeigte Interesse an einer allgemeinen "nearest date" Lösung:

UPDATE 
    Bill 
SET 
    ReportingCurrency = r.ExchangeRate * b.Amount 
FROM 
    Bill b 
    LEFT JOIN FxRate r ON r.RateDate = (
    SELECT TOP 1 RateDate 
    FROM   FxRate 
    ORDER BY  ABS(DATEDIFF(d, RateDate, b.BillDate)), RateDate 
) 

würde das auch sein ziemlich langsam. Alternative:

UPDATE 
    Bill 
SET 
    ReportingCurrency = CASE 
         WHEN DATEDIFF(d, r1.RateDate, b.BillDate) <= DATEDIFF(d, b.BillDate, r2.RateDate) 
         THEN r1.ExchangeRate 
         ELSE COALESCE(r2.ExchangeRate, r1.ExchangeRate) 
         END * b.Amount 
FROM 
    Bill b 
    LEFT JOIN FxRate r1 ON r1.RateDate = (
    SELECT MAX(RateDate) FROM FxRate WHERE RateDate <= b.BillDate 
) 
    LEFT JOIN FxRate r2 ON r2.RateDate = (
    SELECT MIN(RateDate) FROM FxRate WHERE RateDate >= b.BillDate 
) 
+0

Was wäre, wenn das -> nächste <- RateDate> BillDate wäre? Ich weiß, dass er nicht darum gebeten hat, nur aus Neugier. – Node

+0

Das OP fragte nach dem "nächsten <= RateDate". – Tomalak

+0

Ich weiß. Wie gesagt, nur aus Neugier, wie würdest du das lösen? – Node

1

sollten Sie in der Lage sein, dies mit einer Unterabfrage zu erreichen. Hoffentlich funktioniert mein Beispiel unten unverändert (und ist fehlerfrei!) Der einzige Hinweis ist, dass Sie Ihre Berichtswährung (Basiswährung) für meinen angenommenen "USD" ersetzen müssen.

UPDATE Bill SET ReportingCurrency = (Bill.Amount * 
    (SELECT TOP 1 FxRate.ExchangeRate FROM FxRate 
    WHERE FxRate.SourceCurrency = Bill.BillCurrency 
    AND FxRate.TargetCurrency = 'USD' 
    AND FxRate.RateDate <= Bill.BillDate 
    ORDER BY FxRate.RateDate DESC)) 

Hoffe, das hilft. EDIT - Hinzugefügt ORDER BY Klausel