【问题标题】:Identifying start and end of assignment oracle sql 10g识别分配oracle sql 10g的开始和结束
【发布时间】:2012-11-09 16:09:04
【问题描述】:

这与我在这里已经提出(并已回答)的问题非常相似:Identifying start and end of period covered - oracle 10g sql

但是,在这种情况下,我无法弄清楚如何实现我所需要的。我的数据如下所示:

ID  Start            End    End Code        Worker ID
A   02/08/2003  23/01/2007  A               1
A   24/01/2007  17/11/2008  J               2
A   03/03/2009  20/10/2009  A               3
A   21/10/2009  08/03/2010  A               4
A   09/03/2010  29/07/2010  A               5
A   30/07/2010                              6

结束代码“A”表示已重新分配案例,“J”表示已关闭。我想要如下所示的数据:

     ID Start             End   Worker IDs  End Worker
     A  02/08/2003  17/11/2008  1,2         2
     A  03/03/2009              3,4,5,6     6

正如我已经说过的,这与我之前提出的问题非常相似,但在这种情况下,我可以使用一个启动代码。我想我需要确定分配的开始 - 它要么是带有“J”代码的分配之后的第一个日期,要么只是最早的日期,但我有点挣扎。任何建议表示赞赏!供参考,我的oracle版本是10g,10.2.0.5.0。

编辑:接受@Dazzal 提供的答案,因为它回答了我的原始查询,但我根据@Gordon Linoff 的答案发布了一个新问题,因为我认为这更符合我的需求。 https://stackoverflow.com/questions/13383560/grouping-data-oracle-sql-based-on-sum

【问题讨论】:

  • “工人 ID”的顺序重要吗?
  • 抱歉,刚看到这个。有条理会很好,但不是必需的。我看到你添加了两个解决方案,谢谢!

标签: sql oracle date oracle10g connect-by


【解决方案1】:

如果工人 ID 排序不重要,请使用内置聚合函数。

SQL> select id, min(start_date), max(case when last_worker is not null then end_date end) end_date,
  2         wm_concat(worker_id) worker_ids, max(last_worker) last_worker
  3    from (select id, start_date, end_date, end_code, worker_id, case when end_code ='J' or end_date is null then worker_id end last_worker,
  4                 nvl(max(r) over (partition by id order by end_date nulls last), 1) r
  5    from (select t.*,
  6                  case
  7                    when lag(end_code, 1) over (partition by id order by end_date nulls last) = 'J' then
  8                     row_number() over(partition by id order by end_date nulls last)
  9                  end r
 10             from test t)
 11  )
 12  group by id, r;

I MIN(START END_DATE  WORKER_IDS           LAST_WORKER
- --------- --------- -------------------- -----------
A 02-AUG-03 17-NOV-08 1,2                            2
A 03-MAR-09           3,6,5,4                        6

是这样,您必须创建自己的聚合函数。例如,这是我在 10g 上使用的一个:

SQL> select id, min(start_date), max(case when last_worker is not null then end_date end) end_date,
  2         stragg_num(stragg_num_typ(worker_id, ',', worker_id)) worker_ids, max(last_worker) last_worker
  3    from (select id, start_date, end_date, end_code, worker_id, case when end_code ='J' or end_date is null then worker_id end last_worker,
  4                 nvl(max(r) over (partition by id order by end_date nulls last), 1) r
  5    from (select t.*,
  6                  case
  7                    when lag(end_code, 1) over (partition by id order by end_date nulls last) = 'J' then
  8                     row_number() over(partition by id order by end_date nulls last)
  9                  end r
 10             from test t)
 11  )
 12  group by id, r;

I MIN(START END_DATE  WORKER_IDS           LAST_WORKER
- --------- --------- -------------------- -----------
A 02-AUG-03 17-NOV-08 1,2                            2
A 03-MAR-09           3,4,5,6                        6

SQL>

其中的定义是:

drop function stragg;
drop function stragg_num;
drop type string_agg_type;
drop type stragg_vc_tab;
drop type stragg_vc_typ;
drop type stragg_num_tab;
drop type stragg_num_typ;
create or replace type stragg_vc_typ as object
(
  value   varchar2(4000),
  delim   varchar2(10),
  rown    varchar2(4000)
);
/
create or replace type stragg_vc_tab
as table of stragg_vc_typ;
/
show errors type stragg_vc_tab
create or replace type stragg_num_typ as object
(
  value   varchar2(4000),
  delim   varchar2(10),
  rown    integer
);
/
show errors type stragg_num_typ
create or replace type stragg_num_tab
as table of stragg_num_typ;
/
show errors type stragg_num_tab


create or replace type string_agg_type as object
(
   total clob,
   delim   varchar2(10),
   data    stragg_num_tab,
   data2    stragg_vc_tab,

   static function
        ODCIAggregateInitialize(sctx IN OUT string_agg_type )
        return number,

   member function
        ODCIAggregateIterate(self IN OUT string_agg_type ,
                             value IN stragg_num_typ )
        return number,

   member function
        ODCIAggregateIterate(self IN OUT string_agg_type ,
                             value IN stragg_vc_typ )
        return number,

   member function
        ODCIAggregateTerminate(self IN string_agg_type,
                               returnValue OUT  varchar2,
                               flags IN number)
        return number,

   member function
        ODCIAggregateMerge(self IN OUT string_agg_type,
                           ctx2 IN string_agg_type)
        return number
);
/
show errors type string_agg_type
create or replace type body string_agg_type
is


static function ODCIAggregateInitialize(sctx IN OUT string_agg_type)
return number
is
begin
    sctx := string_agg_type( null, null, null, null );
    return ODCIConst.Success;
end;

member function ODCIAggregateIterate(self IN OUT string_agg_type,
                                     value IN stragg_num_typ )
return number
is
begin
    if (delim is null)
    then
      delim := value.delim;
    end if;
    if (data is null)
    then
      data := stragg_num_tab();
    end if;
    data.extend;
    data(data.last) := value;
    return ODCIConst.Success;
end;

member function ODCIAggregateIterate(self IN OUT string_agg_type,
                                     value IN stragg_vc_typ )
return number
is
begin
    if (delim is null)
    then
      delim := value.delim;
    end if;
    if (data2 is null)
    then
      data2 := stragg_vc_tab();
    end if;
    data2.extend;
    data2(data2.last) := value;
    return ODCIConst.Success;
end;

member function ODCIAggregateTerminate(self IN string_agg_type,
                                       returnValue OUT varchar2,
                                       flags IN number)
return number
is
  v_delim  varchar2(10);
begin
    if data is not null 
    then
      for r_item in (select d.value
                       from table(data) d
                      order by d.rown)
      loop
        returnValue := returnValue || v_delim || r_item.value;
        v_delim := self.delim;
      end loop;
    else
      for r_item in (select d.value
                       from table(data2) d
                      order by d.rown)
      loop
        returnValue := returnValue || v_delim || r_item.value;
        v_delim := self.delim;
      end loop;
    end if;
    return ODCIConst.Success;
end;

member function ODCIAggregateMerge(self IN OUT string_agg_type,
                                   ctx2 IN string_agg_type)
return number
is
begin
    self.total := self.total || ctx2.total;
    return ODCIConst.Success;
end;


end;
/
show errors type body string_agg_type
CREATE or replace FUNCTION stragg_num(input stragg_num_typ )
RETURN varchar2
PARALLEL_ENABLE AGGREGATE USING string_agg_type;
/

CREATE or replace FUNCTION stragg(input stragg_vc_typ )
RETURN varchar2
PARALLEL_ENABLE AGGREGATE USING string_agg_type;
/

【讨论】:

  • 谢谢。我们的系统刚刚停机进行维护,但我将在下周首先测试一下。
  • 嗨,今天早上试过了。它似乎工作得很好,谢谢。它向我展示的是,使用“J”作为结束日期的记录并不像应有的那么可靠,所以我要检查@GordonLinoff 的解决方案,因为它只查看日期而不是代码。谢谢!
  • @bawpie 如果您想使用上面的简单方法按日期而不是数字/varchar 进行订购。你可以这样做:stragg(stragg_vc_typ(worker_id, ',', to_char(your_date_field, 'yyyymmddhh24miss')) 或者当然添加一个日期类型create or replace type stragg_date_typ as object ( value varchar2(4000), delim varchar2(10), rown date); 然后将这个新对象添加到一个函数中+在对象中重载它。 @GordonLinoff 建议使用 listagg,但您说您使用的是 10g,所以这是行不通的,因为它是 11g 的东西。
  • 我会将 stragg(stragg_vc_typ(worker_id, ',', to_char(your_date_field, 'yyyymmddhh24miss')) 放在查询中吗?是要替换 wm_concat 吗?我试过这样做,但是我得到一个异常:DBD,ORA-00904:“STRAGG”:无效标识符错误。谢谢。
【解决方案2】:

您可以通过集合操作来做到这一点。以下标识了没有间隙的时段——即不使用结束代码。它使用lag 函数确定记录是否开始一个周期。然后它会进行累积求和以识别组内的成员,然后进行聚合以将它们聚集在一起。

select groupnum, MIN(start) as start,
       (case when GroupSeqNum = NumInGroup then max(end) end) as end,
       listagg(WorkerId delimiter ',' order by start) as WorkerIds,
       (case when GroupSeqNum = NumInGroup then max(WorkerId) end) as EndWorker
from (select t.*,
             ROW_NUMBER() over (partition by id, groupnum order by start) as GroupSeqNum,
             COUNT(*) over (partition by id, groupnum) as NumInGroup     
      from (select t.*,
                   SUM(IsStart) over (partition by id order by start) as GroupNum
            from (select t.*,
                         (case when lag(end) over (partition by id order by start) = start - 1 then 0 else 1 end) as IsStart
                  from t
                 ) t
           ) t
    ) t

如果您以“J”代码结尾来识别组,只需将 IsStart 的定义更改为:

                     (case when lag(EndCode) over (partition by id order by start) = 'J' then 0 else 1 end) as IsStart

【讨论】:

  • 谢谢,我现在只是在看这个,并认为这最适合我作为解决方案,但是,最后没有 group by,所以它给我一个错误(例外: DBD,ORA-00937:不是单组组函数状态:N/A)。我要么需要在外部查询中加入一些过度的partion by,要么在底部有一个group by,但我不太明白。查看内部查询返回的内容,我可以看到 istart 是正确的。
  • @pawpie 这对你有用吗?正如你所说,你有 10g 没有 listagg (11g) ?
  • @DazzaL - listagg 不起作用,你是对的。但是,拥有工作人员列表并不是一个重要的优先事项,更多的是我不需要依赖用于识别分配结束的“J”代码。
猜你喜欢
  • 2012-10-06
  • 2020-04-30
  • 2017-01-30
  • 1970-01-01
  • 1970-01-01
  • 2015-12-13
  • 1970-01-01
  • 2022-12-01
  • 1970-01-01
相关资源
最近更新 更多