listagg查询结果不同的值

时间:2013-12-09 05:31:10

标签: sql oracle

我有这样的表

+----------------------------+----------------------------+
| Customer_ref(varchar2(40)) | Event_source-varchar2(40)) |
+----------------------------+----------------------------+
| ctx10000012                | 12474748,1247574,1247674   |
| ctx10000013                | 12474748,12474749,12474750 |
+----------------------------+----------------------------+
SELECT LISTAGG(EVENT_SOURCE, ', ') WITHIN GROUP(ORDER BY EVENT_SOURCE) from 
            ( select distinct  customer_ref, EVENT_SOURCE from CUSTEVENTSOURCE where   customer_ref = Acc.Customer_ref AND END_DTM IS NULL) 

表的输出应该像

+----------------------------+----------------------------+
| Customer_ref(varchar2(40)) | Event_source-varchar2(40)) |
+----------------------------+----------------------------+
| ctx10000012                | 12474748,1247574,1247674   |
| ctx10000013                | 12474748 - 12474750        |
+----------------------------+----------------------------+

我如何在单选择查询中执行此操作(不使用with as select())。因为我在游标内使用此选择查询。

感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

此处没有内置功能可以帮助您。因此,自己编写一个名为ranges_string(或任何你喜欢的名字)的PL / SQL函数,将字符串从'1,2,3,5,7,8,9'转换为'1-3,5,7-9'(或者你需要它然后在你的select语句中调用它:

select ranges_string( listagg(event_source, ', ') within group(order by event_source) )
from
(
  select distinct customer_ref, event_source from custeventsource
  where customer_ref = acc.customer_ref and end_dtm is null
);

编辑:这是一个转换为范围的函数(将'1,2,3,6,7,8,10,12,13,14'转换为'1-3,6-8,10,12-例如14')。我没有花太多时间在它上面,所以当然不是很好,所以随意改变它: - )

create or replace function convert_ranges(vi_numbers varchar2) return varchar2 is
  type table_of_integer is table of integer index by binary_integer;
  v_numbers table_of_integer;
  v_ranges varchar2(4000);
  v_pos integer;
  v_count integer;
begin
  -- Tokenize
  select regexp_substr(vi_numbers, '[^,]+', 1, level) as num
  bulk collect into v_numbers
  from dual
  connect by regexp_substr(vi_numbers, '[^,]+', 1, level) is not null;

  -- Collect
  v_ranges := v_numbers(1);
  v_pos := 2;
  while v_pos <= v_numbers.count loop
    v_count := 0; 
    while v_pos <= v_numbers.count and v_numbers(v_pos) = v_numbers(v_pos-1) + 1 loop
      v_count := v_count + 1;
      v_pos := v_pos + 1;
    end loop;
    if v_count > 0 then
      v_ranges := v_ranges || '-' || v_numbers(v_pos-1);
    end if;
    if v_pos <= v_numbers.count then
      v_ranges := v_ranges || ',';
      v_ranges := v_ranges || v_numbers(v_pos);
      v_pos := v_pos + 1;
    end if;
  end loop;

  return v_ranges;
end convert_ranges;