Oracle 如何使用游标和返回表创建流水线函数?

Oracle 如何使用游标和返回表创建流水线函数?,oracle,plsql,pipelined-function,Oracle,Plsql,Pipelined Function,我有这样一个表(这里有9列带“;”。这是示例表): 我需要像这样的目标表: ID column_1 column_2 1 T1 B1 1 T1 B5 1 T1 B10 1 T1 B13 1 T2 B1 1 T2 B5 1 T2 B10 1 T2 B13 1 T3 B1 1

我有这样一个表(这里有9列带“;”。这是示例表):

我需要像这样的目标表:

ID  column_1    column_2
1      T1        B1
1      T1        B5
1      T1        B10
1      T1        B13
1      T2        B1
1      T2        B5
1      T2        B10
1      T2        B13
1      T3        B1
1      T3        B5
1      T3        B10
1      T3        B13
2      T7        B2
2      T7        B3
2      T7        B5
2      T8        B2
2      T8        B3
2      T8        B5
2      T9        B2
2      T9        B3
2      T9        B5
2      T10       B2
2      T10       B3
2      T10       B5
2      T11       B2
2      T11       B3
2      T11       B5
我找到了以下链接: 但我不能定期创建函数。我只为一列创建函数,但不能循环,也不能调用表。以下是我的功能:

create or replace function fun_pipelined(i_str in varchar2)
  RETURN sys.odcivarchar2list PIPELINED
IS
  v_arr     dbms_sql.varchar2_table;
  v_i       long;
  v_cnt     number;
  i         number;
begin
  v_arr := pl.split(nvl(i_str,' ,'),',');
  v_cnt := regexp_count(nvl(i_str,','), ',') + 1;
  i := 1;
  loop
    exit when i > v_cnt;
    v_i := trim(v_arr(i));
    pipe row (v_i);
    i := i+1;
  end loop;
end;

你能给我一些建议吗?谢谢

您可以尝试以下查询:

WITH DATAA AS (
    SELECT DISTINCT
        ID,
        REGEXP_SUBSTR(COLUMN_1, '[^;]+', 1, LEVEL) COLUMN_1,
        REGEXP_SUBSTR(COLUMN_2, '[^;]+', 1, LEVEL) COLUMN_2
    FROM
        MYTABLE
    CONNECT BY REGEXP_SUBSTR(COLUMN_1, '[^;]+', 1, LEVEL) IS NOT NULL
               OR REGEXP_SUBSTR(COLUMN_2, '[^;]+', 1, LEVEL) IS NOT NULL
)
SELECT
    ID,
    COLUMN_1,
    COLUMN_2
FROM
    (
        SELECT DISTINCT
            D1.ID,
            D1.COLUMN_1,
            D2.COLUMN_2
        FROM
            DATAA D1
            JOIN DATAA D2 ON ( D1.ID = D2.ID )
    )
WHERE
    ( COLUMN_1 IS NOT NULL
      AND COLUMN_2 IS NOT NULL )
ORDER BY
    ID,
    COLUMN_1;


干杯

与Oracle的connect by相比,我更喜欢通用表表达式。这是使用CTE得到的结果

WITH
    mytable AS
        (SELECT 1 id, 'T1;T2;T3' column_1, 'B1;B5;B10;B13' column_2 FROM DUAL
         UNION ALL
         SELECT 2 id, 'T7;T8;T9;T10;T11', 'B2;B3;B5' FROM DUAL),
    mytable2 AS( SELECT id, column_1 || ';' AS column_1, column_2 || ';' AS column_2 FROM mytable ),
    splitset1 ( id
              , column_1
              , column_2
              , REMAINDER ) AS
        (SELECT id
              , SUBSTR( column_1
                      , 1
                      , INSTR( column_1, ';' ) - 1 )              AS column1
              , column_2
              , SUBSTR( column_1, INSTR( column_1, ';' ) + 1 )    AS REMAINDER
           FROM mytable2
         UNION ALL
         SELECT id
              , SUBSTR( REMAINDER
                      , 1
                      , INSTR( REMAINDER, ';' ) - 1 )
              , column_2
              , SUBSTR( REMAINDER, INSTR( REMAINDER, ';' ) + 1 )
           FROM splitset1
          WHERE REMAINDER IS NOT NULL),
    splitset2 ( id
              , column_1
              , column_2
              , REMAINDER ) AS
        (SELECT id
              , column_1
              , SUBSTR( column_2
                      , 1
                      , INSTR( column_2, ';' ) - 1 )              AS column2
              , SUBSTR( column_2, INSTR( column_2, ';' ) + 1 )    AS REMAINDER
           FROM splitset1
         UNION ALL
         SELECT id
              , column_1
              , SUBSTR( REMAINDER
                      , 1
                      , INSTR( REMAINDER, ';' ) - 1 )
              , SUBSTR( REMAINDER, INSTR( REMAINDER, ';' ) + 1 )
           FROM splitset2
          WHERE REMAINDER IS NOT NULL)
  SELECT id
       , column_1
       , column_2
    FROM splitset2
ORDER BY id
       , CAST( SUBSTR( column_1, 2 ) AS NUMBER )
       , CAST( SUBSTR( column_2, 2 ) AS NUMBER )
如果您使用的是Oracle 12,则可以使用SQL函数使您的SQL非常可读,但要付出一定的开销:

WITH
    FUNCTION after( p_value IN VARCHAR2, p_separator IN VARCHAR2 DEFAULT ';' )
        RETURN VARCHAR2 AS
        l_pos   INTEGER;
    BEGIN
        l_pos   := INSTR( p_value, p_separator );
        RETURN CASE WHEN l_pos > 0 THEN SUBSTR( p_value, l_pos + 1 ) ELSE NULL END;
    END after;

    FUNCTION before( p_value IN VARCHAR2, p_separator IN VARCHAR2 DEFAULT ';' )
        RETURN VARCHAR2 AS
        l_pos   INTEGER;
    BEGIN
        l_pos   := INSTR( p_value, p_separator );
        RETURN CASE
                   WHEN l_pos > 0
                   THEN
                       SUBSTR( p_value
                             , 1
                             , l_pos - 1 )
                   ELSE
                       p_value
               END;
    END before;

    mytable AS
        (SELECT 1 id, 'T1;T2;T3' column_1, 'B1;B5;B10;B13' column_2 FROM DUAL
         UNION ALL
         SELECT 2 id, 'T7;T8;T9;T10;T11', 'B2;B3;B5' FROM DUAL),
    mytable2 AS( SELECT id, column_1 || ';' AS column_1, column_2 || ';' AS column_2 FROM mytable ),
    splitset1 ( id
              , column_1
              , column_2
              , REMAINDER ) AS
        (SELECT id
              , before( column_1 )     AS column1
              , column_2
              , after( column_1 )      AS REMAINDER
           FROM mytable2
         UNION ALL
         SELECT id
              , before( REMAINDER )
              , column_2
              , after( REMAINDER )
           FROM splitset1
          WHERE REMAINDER IS NOT NULL),
    splitset2 ( id
              , column_1
              , column_2
              , REMAINDER ) AS
        (SELECT id
              , column_1
              , before( column_2 )     AS column2
              , after( column_2 )      AS REMAINDER
           FROM splitset1
         UNION ALL
         SELECT id
              , column_1
              , before( REMAINDER )
              , after( REMAINDER )
           FROM splitset2
          WHERE REMAINDER IS NOT NULL)
  SELECT id
       , column_1
       , column_2
    FROM splitset2
ORDER BY id
       , CAST( SUBSTR( column_1, 2 ) AS NUMBER )
       , CAST( SUBSTR( column_2, 2 ) AS NUMBER )

谢谢,但我的原始表中有9列。因此,我尝试创建一个带有游标的函数。对不起,忘了写了。我现在正在编辑我的文章,通过使用PL/SQL函数,您将降低查询的性能。如果您有9个这样的列,那么您也可以在上面的查询中写入所有列。是的,这样存储数据不是一个好的做法。您必须采取措施改进数据库的设计。不幸的是,我无法更改此表,因为业务部门正在管理该表。我已经尝试创建上面的函数很久了:)因此我想学习如何再次创建此函数,如果您将创建函数,您将增加上下文切换,并且您的查询将无法很好地执行。。性能将降低。关于DB设计,如果它不在你的盘子里,也没关系。:)您使用的是哪个版本的Oracle?我使用Oracle 11g存储数据作为分隔符分隔的值是一种不好的做法,应该避免。规范化数据模型并单独存储值。阅读以了解更多请以文本形式发布所需输出。很多人无法或不会尝试访问图像转储站点中的屏幕截图。谢谢APC。我将输出更改为文本。
WITH
    FUNCTION after( p_value IN VARCHAR2, p_separator IN VARCHAR2 DEFAULT ';' )
        RETURN VARCHAR2 AS
        l_pos   INTEGER;
    BEGIN
        l_pos   := INSTR( p_value, p_separator );
        RETURN CASE WHEN l_pos > 0 THEN SUBSTR( p_value, l_pos + 1 ) ELSE NULL END;
    END after;

    FUNCTION before( p_value IN VARCHAR2, p_separator IN VARCHAR2 DEFAULT ';' )
        RETURN VARCHAR2 AS
        l_pos   INTEGER;
    BEGIN
        l_pos   := INSTR( p_value, p_separator );
        RETURN CASE
                   WHEN l_pos > 0
                   THEN
                       SUBSTR( p_value
                             , 1
                             , l_pos - 1 )
                   ELSE
                       p_value
               END;
    END before;

    mytable AS
        (SELECT 1 id, 'T1;T2;T3' column_1, 'B1;B5;B10;B13' column_2 FROM DUAL
         UNION ALL
         SELECT 2 id, 'T7;T8;T9;T10;T11', 'B2;B3;B5' FROM DUAL),
    mytable2 AS( SELECT id, column_1 || ';' AS column_1, column_2 || ';' AS column_2 FROM mytable ),
    splitset1 ( id
              , column_1
              , column_2
              , REMAINDER ) AS
        (SELECT id
              , before( column_1 )     AS column1
              , column_2
              , after( column_1 )      AS REMAINDER
           FROM mytable2
         UNION ALL
         SELECT id
              , before( REMAINDER )
              , column_2
              , after( REMAINDER )
           FROM splitset1
          WHERE REMAINDER IS NOT NULL),
    splitset2 ( id
              , column_1
              , column_2
              , REMAINDER ) AS
        (SELECT id
              , column_1
              , before( column_2 )     AS column2
              , after( column_2 )      AS REMAINDER
           FROM splitset1
         UNION ALL
         SELECT id
              , column_1
              , before( REMAINDER )
              , after( REMAINDER )
           FROM splitset2
          WHERE REMAINDER IS NOT NULL)
  SELECT id
       , column_1
       , column_2
    FROM splitset2
ORDER BY id
       , CAST( SUBSTR( column_1, 2 ) AS NUMBER )
       , CAST( SUBSTR( column_2, 2 ) AS NUMBER )