2009-03-06 11 views
0

I ont deux tables:tables de base de données Mise à jour lorsque les valeurs sont manquantes

Bill:

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

Taux de Change:

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

Voici ce que je veux faire:

Je veux vous pdate ma table de Bill comme

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

Dans cette mise à jour toutes les lignes qui ont une entrée pour cette date particulière obtenir les nouvelles données de reportingcurrency. Puisque la table de Bill peut avoir plusieurs rangées éligibles pour la mise à jour, j'ai le problème suivant:

Pour les lignes où il n'y avait aucune entrée dans la table de FxRate (pour cette date), le ReportingCurrency devient NULL. Je veux revenir à la nearest <= RateDate et ramasser le taux de change.Est-ce possible en utilisant des modifications dans la même déclaration de mise à jour ou une autre méthode efficace? (Je veux éviter un curseur).

+0

D'oh. Corrigé une erreur stupide dans ma réponse. – Tomalak

Répondre

0

Bien sûr, cela est possible - comme SELECT ce serait:

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 
) 

Par conséquent, une mise à jour:

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 
) 

La solution fait ces hypothèses:

  • il n'est jamais plus qu'un enregistrement FxRate pour un jour donné
  • il y a un dossier qui FxRate tous Bill records antérieure

Si ces hypothèses ne sont pas applicables, la requête doit être logé.

Notez également qu'il s'agit de la syntaxe SQL Server. Il est possible que Sybase soit un peu différent.


Depuis un commentateur a montré un intérêt dans une solution générale "date la plus proche":

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 
) 

Ce serait assez lent, cependant. 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

Et si le -> le plus proche <- RateDate serait> BillDate? Je sais qu'il n'a pas demandé ça, juste par curiosité. – Node

+0

L'OP demandait le "plus proche <= RateDate". – Tomalak

+0

Je sais. Comme je l'ai dit juste par curiosité, comment voulez-vous remédier à cela? – Node

1

Vous devriez être en mesure d'atteindre cet objectif avec une sous-requête. J'espère que mon exemple ci-dessous fonctionnera sans modification (et sera sans erreur!) La seule note est que vous devez remplacer votre devise (de base) de déclaration pour mon «USD» supposé.

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)) 

Espérons que cela aide. EDIT - Ajouté ORDER BY Clause

Questions connexes