2012-04-20 94 views
0

我正在使用具有仅在特定日期内有效的值的汇率数据库。例如,如果我想将美元兑换为欧元,我必须使用特定日期内的特定汇率。汇率随着时间的推移而变化,最终将被另一个更新的汇率所取代。这里是汇率数据库我有一个样本:SQL技巧:在行之间的范围内查找值

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 

因为我们有两个不同的日期,16日和17 2012年4月上述两项交易但我们需要使用被标记为有效起始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表(递归/自引用关系)。第一个有效来源是原始的,而第二个是垂直移动的。结果表是这样的:

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 

我将要使用的有效起始-1场就好像它是一个有效的到现场,这样我可以执行上面的SQL语句。现在注意,Valid-From字段的日期是4月16日,而Valid-To是4月18日。然而在这个阶段,我不知道如何做垂直“转移”记录的递归关系!

请帮忙吗?这并不容易!

回答

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 

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

方法2:

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

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 
+0

请不要强调SQL Server上的循环连接。 – usr 2012-04-20 09:59:28

+0

这样做的问题是,子查询将在每个生成的行上运行,并且我有数十万行要加入。 – prince 2012-04-20 10:08:15

+0

@prince添加方法2使用CROSS APPLY获取To date。 – 2012-04-20 10:41:30

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,那么它应该执行OK。如果您无法更改clusttered索引,请在这些字段上创建一个idex,并在索引中包含exchange-rate - 这也会提供良好的性能。

+0

好主意。我会通过创建一个表函数来清理这两个子查询,该函数只返回当时的费率,或者费率和金额*费率。然后使用交叉应用,传递交易日期和您想要的汇率。然后你可以简单地选择P.Amount,TableFunction.Rate,P.Amount * TableFunction.Rate(或TableFunction.ConvertedAmount) – Tobsey 2012-04-20 10:46:15

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 

然后:

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 
0

窗口功能ANABLE您使用滞后()和铅()函数:

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) 

(更新:增加了日元,和分区)