我很喜欢这个。希望我能在纯sql中完成它,但此时任何解决方案都可以。
我有ta
和tb
个表,其中包含同时发生大约的事件列表。目标是在ta
上找到tb
的“孤儿”记录。 E.g:
create table ta ( dt date, id varchar(1));
insert into ta values( to_date('20130101 13:01:01', 'yyyymmdd hh24:mi:ss') , '1' );
insert into ta values( to_date('20130101 13:01:02', 'yyyymmdd hh24:mi:ss') , '2' );
insert into ta values( to_date('20130101 13:01:03', 'yyyymmdd hh24:mi:ss') , '3' );
create table tb ( dt date, id varchar(1));
insert into tb values( to_date('20130101 13:01:5', 'yyyymmdd hh24:mi:ss') , 'a' );
insert into tb values( to_date('20130101 13:01:6', 'yyyymmdd hh24:mi:ss') , 'b' );
但是,假设我必须使用+ -5秒的阈值。因此,查找查询将类似于:
select
ta.id ida,
tb.id idb
from
ta, tb
where
tb.dt between (ta.dt - 5/86400) and (ta.dt + 5/86400)
order by 1,2
(小提琴:http://sqlfiddle.com/#!4/b58f7c/5)
规则是:
tb
中ta
上给定的最近事件将被视为正确的映射。也就是说,生成的查询应返回类似
的内容IDA | IDB
1 | a
2 | b
3 | null <-- orphan event
虽然我放在这里的示例查询显示了我遇到的问题。当时间重叠时,很难系统地选择正确的行。
dense_rank()
似乎是选择正确行的答案,但是哪些分区/排序会将它们放在正确的位置?
值得一提的是,我在Oracle 11gR2上这样做。
答案 0 :(得分:2)
使用Oracle的分析函数,使用row_number(),lag()和max()的某种组合可能会出现这种情况。但我根本无法绕过它。我一直希望将一个分析函数嵌入到另一个函数中,我认为你不能这样做。您可以使用Common Table Expressions进行步骤,但我无法弄清楚如何使其工作。
但是程序解决方案使用PL * SQL以及一个额外的表来存储结果是相当直接的。我使用row_number()为每个源表中的每一行分配一个按时间顺序排列。你想要一个确定的结果,所以如果你有重复的日期时间,有一个平局断路器是很重要的,因此我的订单是dt,id。这是SQL-Fiddle demo。
或者查看下面的代码:
create table result (
dif number,
ida varchar(1),
idb varchar(1),
dta date,
dtb date
);
declare
prevA integer := 0;
prevB integer := 0;
begin
for rec in (
with
ordered_ta as (
select dt dta,
id ida,
row_number() over (order by dt, id) rowNumA
from ta
),
ordered_tb as (
select dt dtb,
id idb,
row_number() over (order by dt, id) rowNumB
from tb
)
select ta.*,
tb.*,
abs(dta - dtb) * 86400 dif
from ordered_ta ta
join ordered_tb tb
on dtb between (dta - 5/86400) and (dta + 5/86400)
order by rowNumA, rowNumB
)
loop
if rec.rowNumA > prevA and rec.rowNumB > prevB then
prevA := rec.rowNumA;
prevB := rec.rowNumB;
insert into result values (
rec.dif,
rec.ida,
rec.idb,
rec.dta,
rec.dtb
);
end if;
end loop;
end;
/
select * from result
union all
select null dif, id ida, null idb, dt dta, null dtb
from ta
where id not in (select ida from result)
union all
select null dif, null ida, id idb, null dta, dt dtb
from tb
where id not in (select idb from result)
;