Oracle连接子查询的第一行

时间:2012-01-09 17:03:11

标签: sql join oracle11g subquery

这可能看似简单,但不知何故不是。我有一个名为TBL_A的历史汇率数据表,如下所示:

|   id   |  rate  |  added_date  |
|--------|--------|--------------|
|  bill  |  7.50  |   1/24/2011  |
|  joe   |  8.50  |   5/3/2011   |
|  ted   |  8.50  |   4/17/2011  |
|  bill  |  9.00  |   9/29/2011  |

在TBL_B中,我有几个小时需要加入一行TBL_A才能获得成本信息:

|   id   |  hours  |  added_date  |
|--------|---------|--------------|
|  bill  |   10    |   2/26/2011  |  
|  ted   |   4     |   7/4/2011   |
|  bill  |   9     |   10/14/2011 |

正如您所看到的,对于Bill,TBL_A中有两种费率,但它们具有不同的日期。要在一段时间内正确获取Bill的成本,您必须在TBL_A中连续加入TBL_B的每一行,该行适用于该日期。

我认为这很容易;因为这不需要特别快的查询,我可以为每行成本信息做一个单独的子查询。但是,连接的子查询显然无法“看到”它们所连接的其他表。此查询会在子查询中具有“h”别名的任何内容上抛出无效标识符(ORA-00904):

SELECT h.id, r.rate * h.hours as "COST", h.added_date
     FROM TBL_B h
     JOIN (SELECT * FROM (
                SELECT i.id, i.rate 
                     FROM TBL_A i 
                WHERE i.id = h.id and i.added_date < h.added_date
                ORDER BY i.added_date DESC)
           WHERE rownum = 1) r
      ON h.id = r.id

如果问题只是范围界定,我不知道我采取的方法是否可行。但我想在这里做的就是根据一些标准得到一行,所以我肯定对其他方法持开放态度。

编辑:所需的输出将是:

|   id   |   cost  |  added_date  |
|--------|---------|--------------|
|  bill  |   75    |   2/26/2011  |  
|  ted   |   34    |   7/4/2011   |
|  bill  |   81    |   10/14/2011 |

请注意,Bill在表格的两个条目中有两种不同的费率。第一行是10 * 7.50 = 75,第二行是9 * 9.00 = 81。

2 个答案:

答案 0 :(得分:4)

尝试使用not exists

select
    b.id,
    a.rate,
    b.hours,
    a.rate*b.hours as "COST", 
    b.added_date,
    a.added_date
from
    tbl_b b
    inner join tbl_a a on
        b.id = a.id
where
    a.added_date < b.added_date
    and not exists (
        select 
            1 
        from 
            tbl_a a2 
        where 
            a2.added_date > a.added_date 
            and a2.added_date < b.added_date
            and a2.id = a.id 
        )

作为解释原因发生的原因:只有相关的子查询知道它们正在运行的上下文,因为它们是针对每一行运行的。连接子查询实际上是在连接之前执行的,因此它不了解周围的表。您需要返回所有标识信息,以便在查询的顶层进行连接,而不是尝试在子查询中执行此操作。

答案 1 :(得分:1)

select id, cost, added_date from (
select
  h.id,
  r.rate * h.hours as "COST",
  h.added_date,
  -- For each record, assign r=1 for 'newest' rate
  row_number() over (partition by h.id, h.added_date order by r.added_date desc) r
from
  tbl_b h,
  tbl_a r 
where
  r.id = h.id and
  -- Date of rate must be entered before
  -- hours billed:
  r.added_date < h.added_date
)
where r = 1
;