fil*_*ppo 6 sql sorting oracle range dense-rank
我很尴尬.希望我能在纯sql中完成它,但此时任何解决方案都可以.
我有ta和tb表,包含大约同时发生的事件列表.我们的目标是找到"孤儿"的记录ta上tb.例如:
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' );
Run Code Online (Sandbox Code Playgroud)
但是,假设我必须使用+ -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
Run Code Online (Sandbox Code Playgroud)
(小提琴:http://sqlfiddle.com/#!4/ b58f7c/5)
规则是:
tb对于给定的事件,最接近的事件ta将被视为正确的映射.也就是说,生成的查询应该返回类似的内容
IDA | IDB
1 | a
2 | b
3 | null <-- orphan event
Run Code Online (Sandbox Code Playgroud)
虽然我放在这里的示例查询显示了我正在遇到的问题.当时间重叠时,很难系统地选择正确的行.
dense_rank() 似乎是选择正确行的答案,但是分区/排序会将它们放在正确的位置?
值得一提的是,我在Oracle 11gR2上这样做.
看起来这应该可以通过使用 Oracle 分析函数的单个 SQL 语句来实现,也许可以结合 row_number()、lag() 和 max() 来实现。但我就是无法理解它。我一直想将一个分析函数嵌入到另一个分析函数中,但我认为你做不到。您可以使用通用表表达式逐步进行,但我不知道如何使其工作。
但程序解决方案相当简单,使用 PL*SQL 以及一个额外的表来存储结果。我使用 row_number() 为每个源表中的每一行分配时间顺序。您想要一个确定的结果,因此重要的是要有一个平局断路器,以防您有重复的日期时间,因此我的顺序是 dt, id。这是一个SQL-Fiddle 演示。
或者看下面的代码:
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)
;
Run Code Online (Sandbox Code Playgroud)