Oracle SQL。将多行收集到一个没有分析函数,层次结构和递归的行中

时间:2018-01-26 17:44:27

标签: sql oracle

我有一个奇怪的问题。从这个输入:

ID
1
3
6
8
9
11
23

......我需要得到这个输出:

ID    Res
1      1
3      1,3,1
6      1,3,6,3,1
8      1,3,6,8,6,3,1
9      1,3,6,8,9,8,6,3,1
11     1,3,6,8,9,11,9,8,6,3,1

主要问题是我需要在没有 Model子句,分析函数,regexps,CONNECT BY,递归WITH,WM_CONCAT和LISTAGG 的情况下进行查询。这些是业务规则......我想知道是否有办法做到这一点。也许一个函数可以用来收集一些行?或者是我忘记的建筑。任何建议都会有所帮助。

P.S。 PL / SQL也被排除在外

3 个答案:

答案 0 :(得分:4)

这是我发现的唯一方法,可以获得具有所有限制的结果;不是那么优雅,但是......

它的基础是使用XML函数构建串联,然后详细说明生成的字符串以获得结果。

使用此表:

create table tab(ID) as (
    select 1  from dual union all
    select 3  from dual union all
    select 6  from dual union all
    select 8  from dual union all
    select 9  from dual union all
    select 11 from dual union all
    select 23 from dual
)

select id,
       rtrim(
             replace( replace(
                              extract(
                                       xmltype(dbms_xmlgen.getxml
                                             (
                                              'select id from (select 1 pos, id from tab where id <=' || id ||
                                              ' union all select 2, id from tab where id < ' || id || 
                                              ') order by pos, case when pos = 1 then id else -id end')
                                             )
                                       ,'/ROWSET/ROW/ID'
                                     )
                              , '<ID>', '')
                        , '</ID>', ','
                      )
               , ','
              ) as result
from tab t1;

给出:

        ID RESULT
---------- ------------------------------
         1 1
         3 1,3,1
         6 1,3,6,3,1
         8 1,3,6,8,6,3,1
         9 1,3,6,8,9,8,6,3,1
        11 1,3,6,8,9,11,9,8,6,3,1
        23 1,3,6,8,9,11,23,11,9,8,6,3,1

工作原理:内部查询为给定的ID提供所需顺序的ID列表,但是作为行而不是连接值;例如,对于ID = 6,我们有

select id
from (
        select 1 pos, id
        from tab
        where id <= 6
        union all
        select 2, id
        from tab
        where id < 6 
) order by pos,
case when pos = 1 then id else -id end

给出:

        ID
----------
         1
         3
         6
         3
         1

XML部分用于转换每个ID的值列表,在单个值(xmltype)中,replace / trim部分用于从XML中提取数据。

例如,ID=6的XML:

<?xml version="1.0"?>
<ROWSET>
  <ROW>
    <ID>1</ID>
  </ROW>
  <ROW>
    <ID>3</ID>
  </ROW>
  <ROW>
    <ID>6</ID>
  </ROW>
  <ROW>
    <ID>3</ID>
  </ROW>
  <ROW>
    <ID>1</ID>
  </ROW>
</ROWSET>

但是,我认为这更像是一个练习,而不是我希望在真实系统中看到的一段代码。

答案 1 :(得分:0)

只是为了好玩。我使用案例陈述的例子:

create table test (ID number);

insert into test values(1);
insert into test values(3);
insert into test values(6);
insert into test values(8);
insert into test values(9);
insert into test values(11);
insert into test values(23);

commit;

select id,
case id
when 1 then '1'
when 3 then '1,3,1'
when 6 then '1,3,6,3,1'
when 8 then '1,3,6,8,6,3,1'
when 9 then '1,3,6,8,9,8,6,3,1'
when 11 then '1,3,6,8,9,11,9,8,6,3,1'
end as "Res"
from test
where id <> 23
order by id;

输出:

        ID Res
---------- ----------------------
         1 1
         3 1,3,1
         6 1,3,6,3,1
         8 1,3,6,8,6,3,1
         9 1,3,6,8,9,8,6,3,1
        11 1,3,6,8,9,11,9,8,6,3,1

至少它没有使用任何异国情调,这似乎是个问题。我想这是针对某些课程还是考试?

答案 2 :(得分:0)

您可以使用函数创建包,请参阅下面的示例

create table tab_test(ID) as (
    select 1  from dual union all
    select 3  from dual union all
    select 6  from dual union all
    select 8  from dual union all
    select 9  from dual union all
    select 11 from dual
);


create or replace package pkg_test
as
    v_id NUMBER;
    v_ids VARCHAR2(200);
    FUNCTION get_ids (p_id NUMBER) RETURN VARCHAR2;
END;
/

create or replace package body pkg_test
as
    FUNCTION get_ids (p_id NUMBER) RETURN VARCHAR2
    is
        v_rev_text VARCHAR2(200);
    BEGIN
        IF v_ids IS NULL THEN
           v_ids := p_id;
           RETURN v_ids;
        ELSE
           v_ids := v_ids||', '||p_id;
        END IF;
        SELECT substr(reverse(v_ids), length(p_id) + 1)
          into v_rev_text
          from dual;
        RETURN v_ids||', '||trim(',' from trim(v_rev_text));
    END;
END;
/

--Always run this before the select statement,

begin
   pkg_test.v_ids := null;
end;
/

select pkg_test.get_ids(id)
  from tab_test;

输出:

Result
-----------------------
1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
1, 3, 1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
1, 3, 6, 3 ,1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
1, 3, 6, 8, 6 ,3 ,1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
1, 3, 6, 8, 9, 8 ,6 ,3 ,1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
1, 3, 6, 8, 9, 11, 9 ,8 ,6 ,3 ,1                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        

选择了6行