2009-03-06 2 views
0

У меня есть две таблицы:таблицы базы данных Обновление, когда значения отсутствуют

Билл:

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

Это то, что я хочу сделать:

Я хочу бновить мой Билл стол, как

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

В этом обновлении все строки, которые имеют запись для этой конкретной даты получат новые данные reportingcurrency. Поскольку таблица Билла может иметь несколько строк, пригодных для обновления, у меня есть следующая проблема:

Для строк, в которых не было записи в таблице FxRate (для этой даты), ReportingCurrency становится NULL. Я хочу вернуться к nearest <= RateDate и получить обменный курс. Возможно ли, что это будет сделано с использованием изменений в одном и том же заявлении или другом эффективном методе? (Я хочу, чтобы избежать курсора).

+0

D'oh. Исправлена ​​глупая ошибка в моем ответе. – Tomalak

ответ

0

Конечно, это возможно - как SELECT, это было бы:

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 
) 

Таким образом, как 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 
) 

Решение делает эти предположения:

  • там не более одного FxRate запись за любой день
  • есть FxRate записи, которая предшествует все Bill записи

Если эти предположения не применяются, то запрос должен быть размещен.

Также обратите внимание, что это синтаксис SQL Server. Возможно, что Sybase немного отличается.


Поскольку комментатора проявлял интерес к общему решению «ближайшая дата»:

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 
) 

Это будет довольно медленно, хотя. Альтернатива:

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

Что делать, если -> ближайшее <- RateDate будет> BillDate? Я знаю, что он не просил об этом, просто из любопытства. – Node

+0

ОП просил «ближайшего <= RateDate». – Tomalak

+0

Я знаю. Как я сказал только из любопытства, как бы вы решили это? – Node

1

Вы должны быть в состоянии добиться этого с помощью подзапроса. Надеюсь, мой пример ниже будет работать без изменений (и будет без ошибок!). Единственное замечание - вам нужно заменить валюту своей отчетности (базовой) на мой предполагаемый «доллар».

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

Надеюсь, это поможет. EDIT - Добавлено ORDER BY Пункт

Смежные вопросы