【问题标题】:How to generate all the possible combinations of exchange rates in SQL如何在 SQL 中生成所有可能的汇率组合
【发布时间】:2012-11-24 09:43:24
【问题描述】:

给定一个包含货币和汇率元组的表,例如:

EUR CHF 1.20
USD EUR 0.80
CHF JPY 1.30

如何简单地生成货币 (A,B) 和 (B,A) 之间的所有汇率?

我想要以下:

EUR CHF
CHF EUR

EUR USD
USD EUR

USD CHF
CHF USD

具有所有可能的组合,即可以通过链接多个速率从多个速率中得出一个速率

A to B * B to C * C to D = A to D

【问题讨论】:

  • 但您可能需要计算两种货币之间的最短路径?
  • 问题的大小可以忽略不计,当然最短的会更好
  • 有没有可能EUR CHF 不是CHF EUR 的倒数?即,如果价格存在价差,最短路径将是至关重要的,对吧?
  • 您使用的是什么 DBMS?
  • 通过链接,这些汇率给你 1.248 美元 -> 日元,所以链的长度会引入舍入问题(我敢肯定你有超过三个汇率)或矛盾的结果,其中两条不同的路线给出不同的结果。

标签: sql sql-server-2005 join tuples


【解决方案1】:

您可以使用Recursive Common Table Expression 来完成此操作

设置:

Create Table ExchangeRates (
    FromCurrency char(3),
    ToCurrency char(3),
    Rate decimal(10, 2),
    Constraint PK_ExchangeRates Primary Key (FromCurrency, ToCurrency)
);

Create Index IX_ToCurrency On ExchangeRates(ToCurrency, FromCurrency, Rate);

Insert Into ExchangeRates (FromCurrency, ToCurrency, Rate) Values
  ('EUR', 'CHF', 1.20),
  ('USD', 'EUR', 0.80),
  ('CHF', 'JPY', 1.30);

CTE,假定所有货币代码正好是三个字符:

With AllExchanges as (
    Select
        FromCurrency,
        ToCurrency,
        Rate
    From
        ExchangeRates
    Union
    Select
        ToCurrency,
        FromCurrency,
        Cast(1.0 / Rate As Decimal(10, 2))
    From
        ExchangeRates
)
, Paths as (
    Select
        FromCurrency,
        cast(FromCurrency as varchar(max)) As ExchangePath,
        ToCurrency,
        Rate
    From
        AllExchanges a
    Union All
    Select
        p.FromCurrency,
        p.ExchangePath + ',' + p.ToCurrency,
        a.ToCurrency,
        Cast(p.Rate * a.Rate as Decimal(10, 2))
    From
        Paths p
            Inner Join
        AllExchanges a
            On p.ToCurrency = a.FromCurrency
    Where
        p.ExchangePath Not Like '%' + a.ToCurrency + '%'
    )
Select
    FromCurrency,
    ExchangePath + ',' + ToCurrency As ExchangePath,
    ToCurrency,
    Rate
From
    Paths

http://sqlfiddle.com/#!3/0fdc5

【讨论】:

    【解决方案2】:

    我能找到的唯一方法是使用循环,我下面的所有查询都使用此示例数据:

    DECLARE @T TABLE (FromCurrency VARCHAR(3), ToCurrency VARCHAR(3), ExchangeRate DECIMAL(10, 5));
    INSERT @T VALUES
        ('EUR', 'CHF', 1.20),
        ('USD', 'EUR', 0.80),
        ('CHF', 'JPY', 1.30);
    

    第一步是获取所有互惠汇率(即 A -> B 表中的 B -> A),因为这些是最容易获得的:

    DECLARE @TempExchangeRates TABLE (FromCurrency VARCHAR(3), ToCurrency VARCHAR(3), ExchangeRate DECIMAL(10, 5));
    
    INSERT @TempExchangeRates (FromCurrency, ToCurrency, ExchangeRate)
    SELECT  FromCurrency, ToCurrency, ExchangeRate
    FROM    @T
    UNION
    SELECT  ToCurrency, FromCurrency, CAST(1 / ExchangeRate AS DECIMAL(10, 5))
    FROM    @T t
    WHERE   NOT EXISTS
            (   SELECT  1
                FROM    @T t2
                WHERE   t.FromCurrency = t2.ToCurrency
                AND     t.ToCurrency = t2.FromCurrency
            )
    

    此时我们有:

    CHF EUR 0.83333
    CHF JPY 1.30000
    EUR CHF 1.20000
    EUR USD 1.25000
    JPY CHF 0.76923
    USD EUR 0.80000
    

    所以我们还是不见了

    CHF --> USD 
    EUR --> JPY 
    JPY --> CHF 
    JPY --> EUR 
    USD --> EUR 
    

    您需要继续执行此连接

    SELECT  a.FromCurrency, b.ToCurrency, CAST(a.ExchangeRate * b.ExchangeRate AS DECIMAL(10, 5))
    FROM    @TempExchangeRates a
            INNER JOIN @TempExchangeRates b
                ON a.ToCurrency = b.FromCurrency
                AND a.FromCurrency != b.ToCurrency
    WHERE   NOT EXISTS
            (   SELECT  1
                FROM    @TempExchangeRates c
                WHERE   a.FromCurrency = c.FromCurrency
                AND     b.ToCurrency = c.ToCurrency
            )
    

    并插入结果,直到找到所有组合:

    WHILE (1 = 1)
        BEGIN
            INSERT @TempExchangeRates (FromCurrency, ToCurrency, ExchangeRate)
            SELECT  DISTINCT a.FromCurrency, b.ToCurrency, CAST(a.ExchangeRate * b.ExchangeRate AS DECIMAL(10, 5))
            FROM    @TempExchangeRates a
                    INNER JOIN @TempExchangeRates b
                        ON a.ToCurrency = b.FromCurrency
                        AND a.FromCurrency != b.ToCurrency
            WHERE   NOT EXISTS
                    (   SELECT  1
                        FROM    @TempExchangeRates c
                        WHERE   a.FromCurrency = c.FromCurrency
                        AND     b.ToCurrency = c.ToCurrency
                    )
    
            IF @@ROWCOUNT = 0
                BEGIN
                    BREAK;
                END
        END
    

    在第一个循环中,这将检索

    CHF USD 1.04166
    EUR JPY 1.56000
    JPY EUR 0.64102
    USD CHF 0.96000
    

    然后在第二个

    JPY USD 0.80128
    USD JPY 1.24800
    

    那么所有 12 个元组都将被找到。

    【讨论】:

      猜你喜欢
      • 2021-01-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-07
      相关资源
      最近更新 更多