确定创建组的模式

时间:2017-11-09 16:19:16

标签: sql oracle

我的数据如下:

create table t (a varchar2(30), b date);

insert into t values (NULL,TO_DATE('2017/01/01 00:00:44', 'yyyy/mm/dd hh24:mi:ss'));
insert into t values (NULL,TO_DATE('2017/01/01 01:00:44', 'yyyy/mm/dd hh24:mi:ss'));
insert into t values ('AAAA',TO_DATE('2017/01/01 02:00:44', 'yyyy/mm/dd hh24:mi:ss'));
insert into t values (NULL,TO_DATE('2017/01/01 02:30:44', 'yyyy/mm/dd hh24:mi:ss'));
insert into t values ('AAAA',TO_DATE('2017/01/01 03:00:44', 'yyyy/mm/dd hh24:mi:ss'));
insert into t values (NULL,TO_DATE('2017/01/01 04:00:44', 'yyyy/mm/dd hh24:mi:ss'));
insert into t values ('AAAA',TO_DATE('2017/01/01 04:30:44', 'yyyy/mm/dd hh24:mi:ss'));
insert into t values (NULL,TO_DATE('2017/01/01 05:00:44', 'yyyy/mm/dd hh24:mi:ss'));
insert into t values ('AAAA',TO_DATE('2017/01/01 05:30:44', 'yyyy/mm/dd hh24:mi:ss'));

我需要为TEXT字段的每个重复模式分配一个组号。我正在寻找的模式是“AAAA - null - AAAA”。我希望能够忽略此模式之外的任何其他“空值”。实质上,希望分配这样的组号:

a          b                GROUP
(null)     1/1/2017 0:00    IGNORE FROM ASSIGNING GROUP #
(null)     1/1/2017 1:00    IGNORE FROM ASSIGNING GROUP #
AAAA       1/1/2017 2:00      1
(null)     1/1/2017 2:30      1 
AAAA       1/1/2017 3:00      1
(null)     1/1/2017 4:00    IGNORE FROM ASSIGNING GROUP #
AAAA       1/1/2017 4:30      2
(null)     1/1/2017 5:00      2
AAAA       1/1/2017 5:30      2

我希望数据看起来像这样:

a        b                GROUP
AAAA     1/1/2017 2:00    1
(null)   1/1/2017 2:30    1
AAAA     1/1/2017 3:00    1
AAAA     1/1/2017 4:30    2
(null)   1/1/2017 5:00    2
AAAA     1/1/2017 5:30    2

Oracle SQL版本:

Oracle Database 11g 11.2.0.4.0 PL / SQL版本11.2.0.4.0

1 个答案:

答案 0 :(得分:0)

假设:a列中唯一可能的值为'AAAA'null'AAAA'值的数量可能是奇数(在这种情况下,最后一个值不会启动新组,它会被忽略,以及它之前和之后的所有null行。列b中没有重复的日期时间。

有了这些假设:首先,我们使用子查询中的分析'AAAA'来保持count()值的运行计数以及总计数。然后我们调整外部查询的select子句中的组编号(可以从运行计数中轻松计算),并使用运行计数(对于null列中的a)和'AAAA'子句中的总计数(a列中的where),以确定要在输出中保留哪些行。

该代码非常密切地遵循此逻辑解决方案描述。

select a, b, ceil(c/2) as grp
from   (
  select a, b, count(a) over (order by b) as c, count(a) over () as max_c
  from   t
)
where  a is     null and c <  max_c and mod(c, 2) = 1
   or  a is not null and c <= 2 * trunc(max_c/2)
;

A    B                GRP
---- ---------------- ---
AAAA 2017/01/01 02:00   1
     2017/01/01 02:30   1
AAAA 2017/01/01 03:00   1
AAAA 2017/01/01 04:30   2
     2017/01/01 05:00   2
AAAA 2017/01/01 05:30   2

为了好玩,为了说明升级到Oracle 12的好处,以下是使用match_recognize子句(在12.1中添加)非常容易实现的方法:

select a, b, grp
from   t
match_recognize(
  order by b
  measures match_number() as grp
  all rows per match
  pattern  ( h x*? h )
  define   h as h.a = 'AAAA'
);