0

我正在使用一个汇率数据库,该数据库的值仅在特定日期内有效。例如,如果我想将美元兑换成欧元,我必须使用特定日期内的特定汇率。汇率会随着时间而变化,最终会被另一种更新的汇率所取代。这是我拥有的汇率数据库的示例:

Exchange_Rate_History
Valid-From  Exchange-rate  From-Currency  To-Currency
2012-04-16  0.8            USD            EUR
2012-04-18  0.82           USD            EUR
2012-04-20  0.81           USD            EUR  

现在,如果您注意到,我只有一个“有效期至”日期,但我没有“有效期至”日期。

现在我有另一个表需要加入 Exchange_Rate_History 表。此表包含购物交易

Purchases
Transaction-ID  Transaction-Date  Amount-In-USD
1               2012-04-16        100
2               2012-04-17        100

对于上述两个交易,我们有两个不同的日期,即 2012 年 4 月 16 日和 17 日。但是对于这两个日期,我们需要使用标记为 Valid-From 2012-04-16 的汇率。因为我只有一个起始日期,所以我不能使用 BETWEEN..AND 来执行连接。因此,以下联接是不可能的

SELECT 
    * 
FROM
    Exchange_Rate_History
INNER JOIN Purchases ON (Purchases.Transaction-Date BETWEEN Exchange_Rate_History.Valid-From AND Exchange_Rate_History.???)

我正在考虑对 Exchange_Rate_History 表进行自联接(递归/自引用关系),以便获得两个相邻的 Valid-From 列。第一个 Valid-From 将是原始的,而第二个将是垂直移动的。结果表如下所示:

Exchange_Rate_History
Valid-From  Exchange-rate  From-Currency  To-Currency Valid-From-1 (aliased to Valid-To)  
2012-04-16  0.8            USD            EUR         2012-04-18
2012-04-18  0.82           USD            EUR         2012-04-20
2012-04-20  0.81           USD            EUR  

我想使用 Valid-From-1 字段,就好像它是 Valid-To 字段一样,以便我可以执行上面的 SQL 语句。现在请注意,Valid-From 字段的日期为 4 月 16 日,而 Valid-To 的日期为 4 月 18 日。但是在这个阶段我不知道如何做垂直“移动”记录的递归关系!

请问有什么帮助吗?这不是一件容易的事!

4

4 回答 4

1
CREATE FUNCTION GetExchangeRate
(
  @TransactionDate  DATETIME,
  @FromCurrency VARCHAR(3),
  @ToCurrency VARCHAR(3)
 )
RETURNS TABLE
AS
RETURN
    SELECT TOP 1
        Rate
    FROM
        ExchangeRates
    WHERE
        FromCurrency = @fromCurrency
    AND ToCurrency = @toCurrency
    AND ValidFrom <= @transactionDate
    ORDER BY
        ValidFrom DESC

Then:

SELECT
    Purchases.ID,
    Purchases.Transaction-Date,
    Purchases.Amount-In-USD,
    ExchangeRate.Rate,
    Purchases.Amount-In-USD * ExchangeRate.Rate As ConvertedAmount
FROM
    Purchases
    CROSS APPLY dbo.GetExchangeRate(Purchases.Transaction-Date, 'USD', 'EUR') ExchangeRate
于 2012-04-20T11:02:12.120 回答
1

假设每个汇率从下一个开始时开始有效,valid-from并在下一个开始时结束,您希望选择valid-from比交易日期早的最新汇率。那将是当时最新的行。

您可以使用子查询直接获取交易的汇率,如下所示。假设变量@currencyFrom和:@currencyTo@transactionDate

select top 1 
  exchange-rate 
from Exchange_Rate_History erh
where erh.currency-from = @currencyFrom
  and erh.currency-to = @currencyTo
  and erh.valid-from < @transactionDate
order by erh.valid-from desc

您可以将其放在子查询中,通过用外部字段名称替换变量来获取交易的费率。

例如:

Select p.Transaction-ID, p.Transaction-Date, p.Amount-in-USD,
(select top 1 
  exchange-rate 
from Exchange_Rate_History erh
where erh.currency-from = 'USD'
  and erh.currency-to = 'CHF'
  and erh.valid-from < p.Transaction-Date
order by erh.valid-from desc) as exchange-rate,
(select top 1 
  exchange-rate 
from Exchange_Rate_History erh
where erh.currency-from = 'USD'
  and erh.currency-to = 'CHF'
  and erh.valid-from < p.Transaction-Date
order by erh.valid-from desc) * p.Amount-In-USD as Amount-In-CHF


from Purchases p

即使您正在执行两个子查询,您可能会发现它执行得很好。你可以重写它来避免这种情况,但它可能不值得付出努力。

没有办法避免循环连接,但是你的聚集索引应该在currency-from, currency-to,上valid-from,然后它应该可以执行。如果您无法更改聚集索引,请在这些字段上创建一个 idex 并将其也包含exchange-rate在索引中 - 这也将提供良好的性能。

于 2012-04-20T10:31:45.867 回答
0

窗口函数使您能够使用 lag() 和 lead() 函数:

DROP SCHEMA tmp CASCADE;

CREATE SCHEMA tmp ;
SET search_path='tmp';

CREATE TABLE exchange
        ( valid_from DATE NOT NULL
        , cur_from CHAR(3)
        , cur_to CHAR(3)
        , rate FLOAT
        ,PRIMARY KEY(cur_from,cur_to,valid_from)
        );
INSERT INTO exchange(valid_from,rate,cur_from,cur_to) VALUES
  ('2012-04-16',  0.8,        'USD', 'EUR' )
, ('2012-04-18',  0.82,       'USD', 'EUR' )
, ('2012-04-20',  0.81,       'USD', 'EUR' )
, ('2012-04-16',  800,        'USD', 'YEN' )
, ('2012-04-18',  820,       'USD', 'YEN' )
, ('2012-04-20',  810,       'USD', 'YEN' )
        ;

WITH next AS (
        SELECT valid_from
        , lead (valid_from) OVER nxt AS valid_to
        , cur_from
        , cur_to
        , rate
        FROM exchange
        WINDOW nxt AS (
           PARTITION BY cur_from,cur_to
           ORDER BY cur_from,cur_to,valid_from)
           )
SELECT * from next
        ;

结果:

NOTICE:  drop cascades to table tmp.exchange
DROP SCHEMA
CREATE SCHEMA
SET
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "exchange_pkey" for table "exchange"
CREATE TABLE
INSERT 0 6
 valid_from |  valid_to  | cur_from | cur_to | rate 
------------+------------+----------+--------+------
 2012-04-16 | 2012-04-18 | USD      | EUR    |  0.8
 2012-04-18 | 2012-04-20 | USD      | EUR    | 0.82
 2012-04-20 |            | USD      | EUR    | 0.81
 2012-04-16 | 2012-04-18 | USD      | YEN    |  800
 2012-04-18 | 2012-04-20 | USD      | YEN    |  820
 2012-04-20 |            | USD      | YEN    |  810
(6 rows)

(更新:添加了日元和 PARTITION BY)

于 2012-04-20T12:02:06.913 回答
0

下面的呢?

SELECT *
    , (SELECT TOP 1 Valid-From
        FROM Exchange_Rate_History ex2
        WHERE ex2.From-Currency = ex1.From-Currency
            AND ex2.To-Currency = ex1.To-Currency
            AND ex2.Valid-From > ex1.Valid-From
        ORDER BY ex2.Valid-From ASC) Valid-To
FROM Exchange_Rate_History ex1

关于如何处理它可能还有许多其他选项,但这是一个简单的子查询方法。

方法二:

使用交叉应用可能会提高效率。

SELECT *
FROM Exchange_Rate_History ex1
    CROSS APPLY (SELECT TOP 1 Valid-From
        FROM Exchange_Rate_History ex2
        WHERE ex2.From-Currency = ex1.From-Currency
            AND ex2.To-Currency = ex1.To-Currency
            AND ex2.Valid-From > ex1.Valid-From
        ORDER BY ex2.Valid-From ASC) Valid-To
于 2012-04-20T09:47:20.190 回答