Oracle - 列到所有组合行

时间:2011-05-11 13:41:36

标签: sql regex oracle plsql

我有一个带有varchar(100)列的oracle表,我需要根据该列中的字符串组合分开行。字符串分隔符是''(空格),字符串数是可变的。

这是一个例子:

select 1 as ID, 'string_1 string_2 string_3 string_N' as NAME from dual

我需要的输出:

ID  Name
--  ------
1, 'string_1 string_2 string_3 string_N'
1, 'string_1 string_2 string_3'
1, 'string_1 string_2'
1, 'string_1'
1, 'string_2 string_3 string_N'
1, 'string_2 string_3'
1, 'string_2'
1, 'string_3 string_N'
1, 'string_3'
1, 'string_N'

*这是我想要的最小输出,我也可以处理所有可能的组合。

2 个答案:

答案 0 :(得分:1)

SQL> create table mytable (id,name)
  2  as
  3  select 1, 'string_1 string_2 string_3 string_N' from dual union all
  4  select 2, null from dual union all
  5  select 3, 'Ma Lo' from dual
  6  /

Table created.

SQL> with t as
  2  ( select id
  3         , name
  4         , i
  5      from mytable
  6     model
  7           return updated rows
  8           partition by (id)
  9           dimension by (0 i)
 10           measures (name)
 11           ( name[for i from 1 to regexp_count(name[0],' ')+1 increment 1]
 12             = regexp_substr(name[0],'[^ ]+',1,cv(i))
 13           )
 14  )
 15  , t2 (id,name,i) as
 16  ( select id
 17         , name
 18         , i
 19      from t
 20     union all
 21    select t.id
 22         , t.name || ' ' || t2.name
 23         , t.i
 24      from t
 25         , t2
 26     where t.id = t2.id
 27       and t.i < t2.i
 28  )
 29  select id
 30       , name
 31    from t2
 32   order by id
 33       , i
 34       , length(name) desc
 35  /

        ID NAME
---------- --------------------------------------------------
         1 string_1 string_2 string_3 string_N
         1 string_1 string_2 string_N
         1 string_1 string_3 string_N
         1 string_1 string_2 string_3
         1 string_1 string_3
         1 string_1 string_N
         1 string_1 string_2
         1 string_1
         1 string_2 string_3 string_N
         1 string_2 string_3
         1 string_2 string_N
         1 string_2
         1 string_3 string_N
         1 string_3
         1 string_N
         3 Ma Lo
         3 Ma
         3 Lo

18 rows selected.

由于使用了递归子查询因子,因此需要11.2。

的问候,
罗布。

答案 1 :(得分:1)

首先,创建表格和数据:

create table mytable (id,name)  as
(
  select 1, 'string_1 string_2 string_3 string_N' from dual 
  union all
  select 2, null from dual 
  union all
  select 3, 'Ma Lo' from dual
);

其次,创建我们需要的对象和表类型:

CREATE OR REPLACE TYPE name_tbl AS TABLE OF VARCHAR2(100);
CREATE OR REPLACE TYPE id_name_rec AS OBJECT ( id NUMBER, name VARCHAR2(100));
CREATE OR REPLACE TYPE id_name_tbl AS TABLE OF id_name_rec;

第三,创建一个我们将递归调用以解析名称值的函数:

CREATE OR REPLACE
FUNCTION parse_name ( v_name IN VARCHAR2 )
    RETURN name_tbl
IS
    tbl      name_tbl;
    subtbl   name_tbl;
    subname  VARCHAR2(100);
    thisname VARCHAR2(100);
    num      INT := 1;
    idx      INT := 1;
    idxspace INT := 0;
BEGIN
    IF v_name IS NOT NULL THEN
        tbl := name_tbl();

        -- find the number of values
        FOR ws IN 1 .. LENGTH(v_name) loop
            IF ( substr(v_name,ws,1) = ' ' ) THEN
                num := num + 1;
            END IF;
        END loop;

        IF ( num > 1 ) THEN
            -- increase tbl size
            -- get the index of the first whitespace
            idxspace := instr( v_name, ' ');
            -- get thisname
            thisname := substr( v_name, 1, idxspace-1 );
            -- substring name and make recursive call;
            subname  := substr( v_name, idxspace+1 );
            subtbl   := parse_name (subname);

            FOR i IN 1 .. subtbl.count()
            loop
                tbl.EXTEND;
                -- add each subtbl value to tbl
                tbl( tbl.count ) := subtbl(i);
                tbl.EXTEND;
                -- now prepend this name to each value of subtbl and add to tbl
                tbl( tbl.count ) := thisname||' '||subtbl(i);
            END loop;
        ELSE 
            thisname := v_name;
        END IF;
        tbl.EXTEND;
        tbl (tbl.count) := thisname;
    END IF;
    RETURN tbl;
exception
    WHEN others THEN dbms_output.put_line('whoops: '||sqlerrm);
END;

第四,创建返回refcursor的main函数:

create or replace
FUNCTION parse_mytable_name 
    RETURN sys_refcursor
IS
    retcur sys_refcursor;
    idnametbl  id_name_tbl := id_name_tbl();
    valuetbl name_tbl;
BEGIN
    -- get all the records from mytable
    FOR z IN ( SELECT ID, NAME FROM mytable )
    loop
        valuetbl := parse_name(z.NAME);
        IF ( valuetbl IS NOT NULL ) THEN
            FOR i IN 1 .. valuetbl.count 
            loop
                idnametbl.EXTEND;
                idnametbl( idnametbl.count ) := id_name_rec( z.ID, valuetbl(i));
            END loop;
        END IF;
    END loop;

    OPEN retcur FOR SELECT ID, NAME FROM TABLE (idnametbl) order by id, name;

    RETURN retcur;

exception WHEN others THEN dbms_output.put_line('whoops in parse_mytable_name: '||sqlerrm);
END parse_mytable_name;

第五,测试用例pl / sql:

DECLARE
    retcur sys_refcursor;
    testid INT;
    testname varchar2(100);
BEGIN
    retcur := parse_mytable_name();
    loop
        fetch retcur INTO testid,testname;
            exit WHEN retcur%notfound;
        dbms_output.put_line('testid: '||testid||', testname: '||testname);
    END loop;
EXCEPTION
    WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('whoops: '||SQLERRM);
END;

最后,测试结果:

testid: 1, testname: string_1
testid: 1, testname: string_1 string_2
testid: 1, testname: string_1 string_2 string_3
testid: 1, testname: string_1 string_2 string_3 string_N
testid: 1, testname: string_1 string_2 string_N
testid: 1, testname: string_1 string_3
testid: 1, testname: string_1 string_3 string_N
testid: 1, testname: string_1 string_N
testid: 1, testname: string_2
testid: 1, testname: string_2 string_3
testid: 1, testname: string_2 string_3 string_N
testid: 1, testname: string_2 string_N
testid: 1, testname: string_3
testid: 1, testname: string_3 string_N
testid: 1, testname: string_N
testid: 3, testname: Lo
testid: 3, testname: Ma
testid: 3, testname: Ma Lo