如何解决Oracle SQL,联接表的最新日期不超过参考日期
在Oracle sql(版本11g)中,我想将表A与表B连接起来,从B中获取具有最近日期但不位于A中日期之后的行。 示例:
table A table B
| year | id | | year | id | val |
| 2000 | 'a'| | 2000 | 'a'| 1 |
| 2000 | 'b'| | 2001 | 'a'| 2 |
| 2000 | 'c'| | 1999 | 'b'| 1 |
| 2003 | 'c'| | 2003 | 'c'| 1 |
期望的结果是
| year | id | val |
| 2000 | 'a' | 1 |
| 2000 | 'b' | 1 |
| 2000 | 'c' | (null)|
| 2003 | 'c' | 1 |
最简单的方法是只在“ id”列上连接两个表,然后取A和B的“ year”之差,只保留该差最小的行。对于每个“ id”(使用窗口函数)。但是,这需要首先创建一个很大的表,一个非常长的操作。
我想知道是否有更有效的方法,也许使用半联接(= exist子句)。
编辑:建议使用LEFT JOIN LAteraL,这似乎是一个很好的解决方案,但可以从Oracle 12c版获得。我需要一个适用于以前版本的解决方案。
解决方法
横向连接对此很方便:
select a.*,b.*
from a left join lateral
(select b.*
from b
where b.id = a.id and b.year <= a.year
fetch first 1 row only
) b;
编辑:
您只需要b
中的一列,因此请使用相关子查询:
select a.*,(select b.val
from b
where b.id = a.id and b.year <= a.year
fetch first 1 row only
) b
from a;
,
在11g中使用两步法。第一步,简单地将两个表结合在一起,并限制年份。
select a.id,a.year,b.year b_year,b.val
from a
left outer join b
on a.id = b.id and a.year >= b.year
;
I YEAR B_YEAR VAL
- ---------- ---------- ----------
a 2000 2000 1
b 2000 1999 1
c 2003 2003 1
c 2000
每个a.id
和a.year
将获得更多行,因此在第二步中,您仅过滤b.year
最低的行-这是通过ROW_NUMBER
完成的并在第一行(rn = 1
)上进行过滤
with ab as (
select a.id,b.val,row_number() over (partition by a.id,a.year order by b.year) as rn
from a
left outer join b
on a.id = b.id and a.year >= b.year
)
select
id,year,val
from ab
where rn = 1
I YEAR VAL
- ---------- ----------
a 2000 1
b 2000 1
c 2000
c 2003 1
正如您所提到的,如果有很多版本要加入,则这种方法可能是禁止的。我将通过交换您的条件来模拟它,以得到最高的年份(因为没有很多较低的年份可用)。
此处示例数据
create table a as
select 'a' id,2000 year from dual union all
select 'b' id,2000 year from dual union all
select 'c' id,2003 year from dual;
drop table b;
create table b as
select 'a' id,1000 + rownum year,rownum val from dual connect by level <= 1000000
union all
select 'b',rownum val from dual connect by level <= 1000000
union all
select 'c',2000 + rownum year,rownum val from dual connect by level <= 2
;
实际上,以前的方法会导致大量中间结果
with ab as (
select a.id,b.val
from a
left outer join b
on a.id = b.id and a.year <= b.year
)
select count(*) from ab;
1998005
那么什么是替代解决方案?
简单的在第一步中预先计算最小的最高年份
select a.id,min(case when b.year >= a.year then b.year end) b_year
from a
left outer join b
on a.id = b.id
group by a.id,a.year
I YEAR B_YEAR
- ---------- ----------
c 2000 2001
a 2000 2000
c 2003
b 2000 2000
,并将预先计算的表用于与b
with ab as (
select a.id,a.year)
select
ab.id,ab.year,b.val
from ab
left outer join b
on ab.id = b.id and ab.b_year = b.year;
I YEAR VAL
- ---------- ----------
c 2000 1
a 2000 1000
c 2003
b 2000 1000
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。