如何进行涉及子查询的左连接?

时间:2013-09-23 16:39:25

标签: sql join sybase

请帮助解决我在单个查询中正确加入几个时遇到的问题。

简化架构如下:

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 个答案:

答案 0 :(得分:1)

这看起来很像T-SQL Subquery Max(Date) and Joins。那没用吗?

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

答案 1 :(得分:1)

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

答案 2 :(得分: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小提琴示例:http://sqlfiddle.com/#!12/63ef4/1
(它使用的是Postgres和ANSI SQL,但我认为查询也应该在Sybase上运行)

答案 3 :(得分: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子查询集中在范围内的日期和&lt; = transaction_date上,这样可以简化事情。