Oracle 11.2 SQL - 帮助压缩有序集

时间:2016-02-01 09:57:02

标签: sql oracle gaps-and-islands

我有一个带有时间戳列和多个标识符列的数据集。当按时间戳排序时,我希望将具有相同标识符的相邻行的每个“块”压缩为单行。需要每个块的最小和最大时间戳。

来源数据:

TSTAMP  ID1  ID2
t1      A    B  <= start of new block
t2      A    B
t3      C    D  <= start of new block
t4      E    F  <= start of new block
t5      E    F
t6      E    F
t7      A    B  <= start of new block
t8      G    H  <= start of new block

期望的结果:

MIN_TSTAMP  MAX_TSTAMP  ID1  ID2
t1          t2          A    B
t3          t3          C    D
t4          t6          E    F
t7          t7          A    B
t8          t8          G    H

我认为这对于窗口分析函数已经成熟,但是如果没有对IDn的所有相等组合进行分组,而不是仅按相邻行中的那些进行分组,则按时间戳排序时,我无法进行分区。

解决方法是首先在内联视图中创建一个键列,然后我可以将其分组,即块中每行的值相同,每个块的值不同。我可以使用LAG分析函数来比较行值,然后调用PL / SQL函数来返回序列的nextval / currval值(在此上下文中直接调用SQL中的nextval / currval)。

select min(ilv.tstamp), max(ilv.tstamp), id1, id2
from (
  select case when (id1 != lag(id1,1,'*') over (partition by (1) order by tstamp) 
                 or id2 != lag(id2,1,'*') over (partition by (1) order by tstamp))
           then
             pk_seq_utils.gav_get_nextval
           else
             pk_seq_utils.gav_get_currval
           end ident, t.*
  from tab1 t
  order by tstamp) ilv
group by ident, id1, id2
order by 1;

其中gav_get_xxx函数只是从序列返回currval / nextval。

但我想只使用SQL并避免使用PL / SQL(因为我也可以在PL / SQL中轻松编写它并从管道函数中输出结果行。)

有什么想法吗?

感谢。

4 个答案:

答案 0 :(得分:4)

Tabibitosan救援!

with sample_data as (select 't1' tstamp, 'A' id1, 'B' id2 from dual union all
                     select 't2' tstamp, 'A' id1, 'B' id2 from dual union all
                     select 't3' tstamp, 'C' id1, 'D' id2 from dual union all
                     select 't4' tstamp, 'E' id1, 'F' id2 from dual union all
                     select 't5' tstamp, 'E' id1, 'F' id2 from dual union all
                     select 't6' tstamp, 'E' id1, 'F' id2 from dual union all
                     select 't7' tstamp, 'A' id1, 'B' id2 from dual union all
                     select 't8' tstamp, 'G' id1, 'H' id2 from dual)
select   min(tstamp) min_tstamp, max(tstamp) max_tstamp, id1, id2
from     (select tstamp,
                 id1,
                 id2,
                 row_number() over (order by tstamp) - row_number() over (partition by id1, id2 order by tstamp) grp
          from   sample_data)
group by id1,
         id2,
         grp
order by min(tstamp);

MIN_TSTAMP MAX_TSTAMP ID1 ID2
---------- ---------- --- ---
t1         t2         A   B  
t3         t3         C   D  
t4         t6         E   F  
t7         t7         A   B  
t8         t8         G   H  

答案 1 :(得分:2)

您应该能够使用row_number窗口功能执行此操作,如下所示:

select 
    min(tstamp) mints, max(tstamp) maxts, id1, id2
from (
    select 
       *, 
       row_number() over (order by tstamp) 
     - row_number() over (partition by id1, id2 order by tstamp) as rn
    from t
) as subq
group by id1, id2, rn
order by rn

我还没有能够使用任何Oracle数据库进行测试,但它可以与MSSQL一起使用,也可以在Oracle中工作,因为窗口函数的工作方式相同。

答案 2 :(得分:2)

您可以使用an analytic 'trick'来识别间隙和岛屿,将所有行中每行的位置与tstamp的位置进行比较,其中tstamp的位置仅针对id2, id2 select tstamp, id1, id2, row_number() over (partition by id1, id2 order by tstamp) - row_number() over (order by tstamp) as block_id from tab1; TS I I BLOCK_ID -- - - ---------- t1 A B 0 t2 A B 0 t3 C D -2 t4 E F -3 t5 E F -3 t6 E F -3 t7 A B -4 t8 G H -7 组合:

block_id

select min(tstamp) as min_tstamp, max(tstamp) as max_tstamp, id1, id2 from ( select tstamp, id1, id2, row_number() over (partition by id1, id2 order by tstamp) - row_number() over (order by tstamp) as block_id from tab1 ) group by id1, id2, block_id order by min(tstamp); MI MA I I -- -- - - t1 t2 A B t3 t3 C D t4 t6 E F t7 t7 A B t8 t8 G H 的实际值并不重要,只是它对于组合的每个块都是唯一的。然后,您可以使用它进行分组:

 body {
            background:url('bg.jpg') no-repeat center center;
            background-size:cover;  

            /* Workaround for some mobile browsers */
            min-height:100%;    
            padding-top: 20px;
            padding-bottom: 20px;
        }    

答案 3 :(得分:1)

您需要一步一步地执行此操作:

  1. 使用LAG标记每次更改并使用标记= 1检测ID更改。
  2. 为ID组更改标记(运行总计)生成具有SUM的组(即具有相同ID的相邻记录)的密钥。
  3. 按生成的组密钥分组并获取最小/最大时间戳。
  4. 查询:

    SELECT DISTINCT aya.aya_id,
      aya_name,
      (SELECT aya_name 
       FROM AYANTDROIT aya2 
       WHERE aya2.aya_id = 
          (SELECT ben_ben_id 
           FROM benefice LEFT OUTER JOIN ayantdroit ayad 
                          ON ben_aya_id = ayad.aya_id 
                          WHERE ayad.aya_id = **aya.AYA_ID**
          )
       )
    FROM AYANTDROIT aya
    ORDER BY aya_name