1

请帮助解决我在单个查询中正确连接几个表时面临的问题。

简化架构如下:

TRANSACTION TABLE:
-----------------

TRANSACTION_DATE     AMOUNT CURRENCY
---------------- ---------- --------
22/09/13                100 EUR      
22/09/13                200 FRA      
22/09/13                200 GBP   

CURRENCY_CONVERSION TABLE:
-------------------------

FROM_CURRENCY TO_CURRENCY CONVERSION_DATE CONVERSION_RATE
------------- ----------- --------------- ---------------
GBP           USD         23/09/13                   1.61 
EUR           USD         22/09/13                   1.35 

目前,有效的查询如下(使用 Sybase/SQL Server 连接语法)

SELECT  
    t.transaction_date, 
    t.amount, 
    t.currency, 
    t.amount * cc.conversion_rate as amount_usd
FROM transaction t, currency_conversion cc, 
WHERE t.transaction_date *= cc.conversion_date
AND t.currency *= cc.from_currency
AND cc.to_currency = 'USD'

当 currency_conversion 表在某些日期可能没有转换率时,我们在上述查询中遇到问题。在这种缺失值的情况下,企业希望求助于 DB 中可用的最新转换率。例如,如果今天没有欧元兑美元汇率,请使用昨天的汇率。我尝试按如下方式执行此操作:

SELECT  
    t.transaction_date, 
    t.amount, 
    t.currency, 
    t.amount * cc.conversion_rate as amount_usd
FROM transaction t, currency_conversion cc, 
WHERE t.transaction_date *= 
                          (SELECT max(c1.conversion_date) FROM currency_conversion c1 
                          WHERE c1.from_currency = t.from_currency
                          AND c1.to_currency = 'USD')
AND t.currency *= cc.from_currency
AND cc.to_currency = 'USD'

此查询不起作用。t.transaction_date 上的左连接是非法的。如果我放弃加入,它会起作用,但我会在最终结果中错过一些交易。

请指教。

请注意:

  • 在单个查询中实现这一点很重要(两个查询的两步解决方案无济于事)
    • 使用的数据库是 Sybase 12 虽然我认为这是一个通用的 SQL 东西
    • 这是一个简化的模式,在我使用的实际查询中,不同金额需要另外两次这样的美元转换
    • 如果可能,请围绕现有查询提出一个解决方案,只需进行最少的更改

谢谢你的时间。

4

4 回答 4

1

这看起来很像T-SQL Subquery Max(Date) 和 Joins。这没有帮助吗?

(对不起,我还不能评论!)

于 2013-09-23T16:50:31.657 回答
1

子查询不是在两个表之间进行连接,它只是选择一个日期......这不是您真正想要的操作。

如果我理解正确,您想为最近日期的记录选择一个转换率,然后在您的乘法中使用它。

SELECT  
    t.transaction_date, 
    t.amount, 
    t.currency, 
    t.amount * (SELECT cc.conversion_rate FROM currency_conversion cc
                    WHERE cc.from_currency = t.from_currency
                    AND cc.to_currency = 'USD'
                    AND cc.conversion_date = (SELECT max(c1.conversion_date) FROM
                                                  FROM currency_conversion c1
                                                  WHERE c1.conversion_date <=
                                                        t.transaction_date)) as amount_usd
    FROM transaction t

如果您的 currency_conversion 表有开始日期和结束日期,您可以去掉 c1 子查询,只需将 cc 子查询集中在范围内的日期和 <= transaction_date,这将简化事情。

于 2013-09-23T18:30:44.990 回答
1

据我所知 Sybase 也支持窗口函数。我能想到的最好的是:

select transaction_date, 
       amount,
       currency,
       conversion_rate,
       conversion_date as effective_conversion_date,
       amount_usd
from (
  select t.transaction_date, 
         t.amount, 
         t.currency, 
         cc.conversion_rate,
         cc.conversion_date,
         t.amount * cc.conversion_rate as amount_usd,
         dense_rank() over (partition by transaction_date, currency order by cc.conversion_date desc) as rn,
         max(cc.conversion_date) over (partition by currency) as max_conv_date
  from transactions t
    left join currency_conversion cc 
            on t.currency = cc.from_currency
           and cc.to_currency = 'USD'
) t
where (conversion_date = max_conv_date or rn = 1)
order by transaction_date;

诀窍是从外部联接中省略 transaction_date 并使用窗口函数即时计算最新的 conversion_date。同时dense_rank()对“最新”转换日期应用一个值。如果该货币有多个 currency_conversions,则将为单个交易返回多行。

然后通过仅选择 transaction_date 等于最新转换日期或具有最新转换日期的行来过滤掉那些不需要的行。

但是:我认为这不是很有效。如果您的表真的很大(我猜这是意料之中的),这可能表现不佳。

这是一个小 SQL Fiddle 示例:http
://sqlfiddle.com/#!12/63ef4/1 (它使用 Postgres 和 ANSI SQL,但我认为查询也应该在 Sybase 上运行)

于 2013-09-23T18:21:23.747 回答
1

制作一个完整的(临时的,如果需要的话)表是否合理?也就是说,生成您可能需要的所有日期的列表,然后用循环填充表格INSERT,这样您就知道您的加入将始终匹配一个伙伴?这是一些额外的前期成本和更长的连接,但它是一个更简单的连接,通过适当的索引应该很快。

于 2013-09-23T16:58:54.720 回答