0

特定の日付内でのみ有効な値を持つ為替レートのデータベースを使用しています。たとえば、USD を EURO に変換したい場合、特定の日付内に収まる特定の為替レートを使用する必要があります。為替レートは時間の経過とともに変化し、最終的にはより更新された別のレートに置き換えられます。これは私が持っている為替レート データベースのサンプルです。

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

上記の 2 つの取引では、2012 年 4 月 16 日と 17 日の 2 つの異なる日付があります。私は開始日しか持っていないので、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 テーブルの自己結合 (再帰的/自己参照関係) を実行して、2 つの Valid-From 列を隣り合わせに取得することを考えていました。最初の Valid-From は元のもので、2 番目は垂直方向にシフトされたものになります。結果のテーブルは次のようになります。

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  

上記の SQL ステートメントを実行できるように、Valid-To フィールドであるかのように Valid-From-1 フィールドを使用したいと考えています。Valid-From フィールドの日付は 4 月 16 日で、Valid-To の日付は 4 月 18 日です。ただし、この段階では、レコードを垂直に「シフト」する再帰的な関係を行う方法がわかりません!

何か助けてください。これは簡単なことではありません!

4

4 に答える 4

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

2 つのサブクエリを実行している場合でも、これは問題なく実行されることがわかるでしょう。それを避けるために書き直すこともできますが、おそらく努力する価値はありません。

ループ結合を回避する方法はありませんが、クラスター化インデックスがcurrency-from, currency-to,にある必要がありvalid-from、正常に実行されるはずです。クラスター化されたインデックスを変更できない場合は、それらのフィールドに idex を作成exchange-rateし、インデックスにも含めます。これにより、パフォーマンスも向上します。

于 2012-04-20T10:31:45.867 に答える
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
于 2012-04-20T11:02:12.120 に答える
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:

クロス適用を使用すると、より効率的になる場合があります。

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 に答える
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 に答える