来自多个表的相关SQL连接查询

时间:2010-03-30 18:33:30

标签: sql mysql ms-access

我有两张桌子,如下图所示。我需要在dateOfPurchase找到有效的exchangeRate。我尝试了一些相关的子查询,但是我很难在子查询中使用相关记录。

我希望解决方案需要遵循这个基本大纲:

  1. 仅选择适用countryCode的exchangeRates
  2. 从1.选择最新的exchangeRate小于dateOfPurchase
  3. 使用2.和purchaTable。
  4. 中的所有字段填写查询表

    我的表格:

    purchasesTable

    > dateOfPurchase    |   costOfPurchase  |   countryOfPurchase
    > 29-March-2010 |   20.00       |   EUR
    > 29-March-2010 |   3000        |   JPN
    > 30-March-2010 |   50.00       |   EUR
    > 30-March-2010 |   3000        |   JPN
    > 30-March-2010 |   2000        |   JPN
    > 31-March-2010 |   100.00      |   EUR
    > 31-March-2010 |   125.00      |   EUR
    > 31-March-2010 |   2000        |   JPN
    > 31-March-2010 |   2400        |   JPN
    

    costOfPurchase与给定countryCode的本地货币无关


    exchangeRateTable

    > effectiveDate |   countryCode |   exchangeRate    
    > 29-March-2010 |   JPN     |   90
    > 29-March-2010 |   EUR     |   1.75
    > 30-March-2010 |   JPN     |   92
    > 31-March-2010 |   JPN     |   91
    

    我正在寻找的查询结果:

    > dateOfPurchase    |   costOfPurchase  |   countryOfPurchase   |   exchangeRate    
    > 29-March-2010 |   20.00       |   EUR         |   1.75
    > 29-March-2010 |   3000        |   JPN         |   90
    > 30-March-2010 |   50.00       |   EUR         |   1.75
    > 30-March-2010 |   3000        |   JPN         |   92
    > 30-March-2010 |   2000        |   JPN         |   92
    > 31-March-2010 |   100.00      |   EUR         |   1.75
    > 31-March-2010 |   125.00      |   EUR         |   1.75
    > 31-March-2010 |   2000        |   JPN         |   91
    > 31-March-2010 |   2400        |   JPN         |   91
    

    因此,例如在结果中,3月31日欧元的汇率为1.75。

    我正在使用Access,但MySQL的答案也可以。

    更新

    修改Allan的答案:

    SELECT dateOfPurchase, costOfPurchase, countryOfPurchase, exchangeRate
    FROM purchasesTable p 
         LEFT OUTER JOIN 
         (SELECT e1.exchangeRate, e1.countryCode, e1.effectiveDate, min(e2.effectiveDate) AS enddate
          FROM exchangeRateTable e1 
               LEFT OUTER JOIN
               exchangeRateTable e2
               ON e1.effectiveDate < e2.effectiveDate AND e1.countryCode = e2.countryCode
         GROUP BY e1.exchangeRate, e1.countryCode, e1.effectiveDate) e 
         ON p.dateOfPurchase >= e.effectiveDate AND (p.dateOfPurchase < e.enddate OR e.enddate is null) AND p.countryOfPurchase = e.countryCode 
    

    我不得不做一些小改动。

1 个答案:

答案 0 :(得分:3)

如果你的exchangeRate表没有任何空白,这很简单:

select dateOfPurchase, costOfPurchase, countryOfPurchase, exchangeRate
from purchasesTable p 
     inner join 
     exchangeRateTable e 
     on p.dateofpurchase = e.effectivedate 
        and p.countryofpurchase = e.countrycode 

如果它确实有间隙(有效速率设置为1/1并且直到1/3才变化,因此1/1速率适用于1/2),然后它会变得更复杂,因为结束日期只是暗示。在这种情况下,以下应该有效:

select dateOfPurchase, costOfPurchase, countryOfPurchase, exchangeRate
from purchasesTable p 
     left outer join 
     (select e1.exchangeRate, e1.countrycode, 
             e1.effectivedate, min(e2.effectivedate) as enddate
      from exchangeRateTable e1 
           left outer join 
           exchangeRateTable e2
           on e1.effective_date < e2.effective_date 
              and e1.countrycode = e2.countrycode
           group by e1.exchangeRate, e1.countrycode, 
              e1.effectivedate) e 
     on p.dateofpurchase >= e.effectivedate
        and (p.dateofpurchase < e.enddate
             or e.enddate is null) 
        and p.countryofpurchase = e.countrycode 

如果您需要使用此解决方案,您可能希望将最里面的查询放在存储的查询中,以简化此操作并使结束日期可用于其他进程。


我们正在做的是从汇率表(e1)获取每个记录并将其加入到同一表(e2)中稍后发生的所有条目中。我们取第二组值中的最小值(min(e2.effectivedate))。

假设您只有三个值:

1/1/2000
1/3/2000
1/5/2000

联接将为您提供以下结果(每个值与所有更大的值组合):

1/1/2000 < 1/3/2000
1/1/2000 < 1/5/2000
1/3/2000 < 1/5/2000
1/5/2000 < [null]

由于没有1/5/2000的值小于并且我们指定了外连接,因此该行将为第二个表设置一个空值。然后我们指定我们只想要第二个表中的最小值,因此结果集减少到:

1/1/2000 < 1/3/2000
1/3/2000 < 1/5/2000
1/5/2000 < [null]

最后,在最外层的连接中,我们告诉查询加入这两个值之间的所有日期。但是,因为一个集合具有空结束日期,所以我们添加一个或条件来忽略该情况下的上限。


我开始从Litwin,et.al。的“Access 95 Developer's Handbook”学习SQL并经常阅读Usenet,所以我的资料来源已经过时......