如何在Oracle中获取连续的记录对

时间:2013-02-13 08:29:14

标签: sql oracle plsql analytics grouping

有人能给我一个棘手的SQL问题的线索。我搜索了类似的问题,最近的方法似乎是这样,但它不是我的问题的解决方案: Oracle - grouping between pairs of records

我知道这可以通过PL / SQL过程实现,但在这种情况下(Oracle)SQL是必要的。 我正在寻找一个Oracle SQL SELECT来隔离ID = 4和5的普通表的特殊记录。该表仅包含2列:ID和DATE 它包含这样的数据:

ID  DATE          REMARK (this row is not part of table!)
---------------   ------------------------------------------
2   01-JAN-2013     
4   02-JAN-2013   A  
7   03-JAN-2013  
5   05-JAN-2013   A  
6   07-JAN-2013  
4   08-JAN-2013   B  
1   11-JAN-2013  
5   12-JAN-2013   B  

... more follows  

ID 4和5的组如果及时跟随,则属于一起。因此,我标记为“A”的行属于一起,“B”也是如此。 两个As和两个B属于一起,因为它们在日期中是连续的。 现在我想要的是一个返回4列的SELECT,i。即一行中的两行A,以及一行中的行B.

所以输出应该如下所示:

ID4 DATE4        ID5 DATE5           Comment (no column, just comment)  
---------------------------------    ------------------------------------
4   02-JAN-2013  5   05-JAN-2013     First set of 4 and 5  
4   08-JAN-2013  5   12-JAN-2013     Second set of 4 and 5  

...more follows  

(列ID4和ID5当然已经过时,仅用于演示目的)

我希望自己能理解?有人有想法吗?


更新: 感谢您的想法和选择,我很抱歉在提出问题时不够清楚。 只应考虑4对和5对,并且只能按日期的升序排列。对于上面的例子,A​​lex Pool和Florin Ghita的解决方案很棒,谢谢! 4er的日期必须小于或等于5er的日期。如果有一个4er或5er没有匹配的伙伴,可以忽略它。

但是这里有另一个包含真实数据和陷阱的示例(这里的解决方案失败了):按时间顺序遍历数据,有2行,连续5行。

4    16.03.2012 17:49:28  A
5    10.05.2012 09:38:56  A1     Either A1 is possible
5    12.06.2012 07:51:03  A2     or A2 whichever is easier to code
4    12.06.2012 08:47:52  B
5    02.08.2012 11:27:43  B
4    03.08.2012 13:24:54  C
5    03.08.2012 14:14:07  C
4    04.08.2012 15:00:00      should be ignored, as there is no following 5er

通缉输出:

4    16.03.2012 17:49:28    5    10.05.2012 09:38:56   (alternat.:  5  12.06.2012 07:51:03)
4    12.06.2012 08:47:52    5    02.08.2012 11:27:43  
4    03.08.2012 13:24:54    5    03.08.2012 14:14:07  

建议的SELECTS失败,因为他们不认为4er必须在5er之前。如果没有相应的合作伙伴,请忽略此行。我没有说清楚,对不起。

非常感谢你 FRIEDHELM

3 个答案:

答案 0 :(得分:2)

与其他评论者一样,我并不完全确定我会关注,但如果您只想查看ID 4和5并希望按日期顺序匹配它们,您可以执行以下操作:

with t as (
    select id, dt, row_number() over (partition by id order by dt) as rn
    from t42
    where id in (4, 5)
)
select t4.id as id4, t4.dt as date4, t5.id as id5, t5.dt as date5,
    case t4.rn when 1 then 'First' when 2 then 'Second' when 3 then 'Third' end
        || ' set of 4 and 5' as "Comment"
from t t4
join t t5 on t5.rn = t4.rn
where t4.id = 4
and t5.id = 5
order by t4.rn;

       ID4 DATE4            ID5 DATE5     Comment             
---------- --------- ---------- --------- ---------------------
         4 02-JAN-13          5 05-JAN-13 First set of 4 and 5  
         4 08-JAN-13          5 12-JAN-13 Second set of 4 and 5 

我现在不确定你是否真的希望返回/显示'评论'......可能不会,这会略微简化它。


对于修改后的要求:

with t as (
    select id, dt, lead(dt) over (partition by id order by dt) as next_dt
    from t42
    where id in (4, 5)
)
select t4.id as id4, t4.dt as date4, t5.id as id5, min(t5.dt) as date5
from t t4
join t t5 on t5.dt > t4.dt and (t4.next_dt is null or t5.dt <= t4.next_dt)
where t4.id = 4
and t5.id = 5
group by t4.id, t4.dt, t5.id
order by t4.dt;

       ID4 DATE4                        ID5 DATE5               
---------- --------------------- ---------- ---------------------
         4 16.03.2012 17:49:28            5 10.05.2012 09:38:56   
         4 12.06.2012 08:47:52            5 02.08.2012 11:27:43   
         4 03.08.2012 13:24:54            5 03.08.2012 14:14:07   

CTE使用LEAD来查看每个ID的下一个日期,这与ID为4时非常相关;如果没有额外的ID 4,那么它可以为null。然后,连接仅查找位于两个ID 4日期之间(或最后一个ID 4日期之后)的ID 5记录。如果您想在第一个结果中使用备用(稍后)ID 5日期,请使用MAX而不是MIN。 (我不是100%关于><=匹配;我试图解释你所说的内容,但如果它不是正确的话你可能需要调整一下。


要解决看起来像9i的错误(根据MOS可能在9.2.0.3或9.2.0.6中修复,但确实取决于你遇到的错误):

select t4.id as id4, t4.dt as date4, t5.id as id5, min(t5.dt) as date5
from (
    select id, dt, lead(dt) over (partition by id order by dt) as next_dt
    from t42
    where id = 4
) t4
join (select id, dt
    from t42
    where id = 5
) t5 on t5.dt > t4.dt and (t4.next_dt is null or t5.dt <= t4.next_dt)
group by t4.id, t4.dt, t5.id
order by t4.dt;

不幸的是,我没有足够的旧版本来测试这个版本。您不必使用t5子选择,您可以直接将主表连接到t4,但我认为这有点清晰。

答案 1 :(得分:1)

你想要的很简单。只需根据ID对您的记录进行排名。

with ranked_data

as (select 
       id, 
        date_col, 
       row_number() over (partition by id order by date_col) as rnk
   from your_table
   where id in (4, 5))

select t4.id as id4, t4.date_col as date4, t5.id as id5, t5.date_col as date5
from ranked_data t4
full outer join ranked_data t5 
  on (t4.rnk=t5.rnk and t4.id=4 and t5.id=5)

答案 2 :(得分:0)

没有使用分析的自我加入就可以了:

SELECT distinct 
         first_value(id) over (partition by rk order by dt), 
         min(dt) over (partition by rk),
         last_value(id) over (partition by rk order by dt rows between unbounded preceding and unbounded following) id5 ,
         max(dt) over (partition by rk) 
FROM (
SELECT id, dt, dense_rank() over (partition by id order by dt) rk 
FROM t
where  id in (4, 5)
)

这会找到与“第一个”日期对应的id值,因此如果4和5的日期顺序相反,那么您将在“4”列中显示5。目前尚不清楚这是否是你想要的。