简体   繁体   中英

Creating a table from a Comma Separated List in Oracle (> 11g) - Input string limit 4000 chars

I have an Oracle (11g) SP which takes a comma separated string of inputs (eg 'Cats, Dogs, Monkeys'). The crux of the code is taken from Tony Andrews' blog and works as expected but it will not accept input strings greater than 4000 characters:

PROCEDURE delimstring_to_table ( p_delimstring IN VARCHAR2
    , p_table OUT VARCHAR2_TABLE
    , p_nfields OUT INTEGER
    , p_delim IN VARCHAR2 DEFAULT ','
)
IS
    v_string VARCHAR2(32767) := p_delimstring;
    v_nfields PLS_INTEGER := 1;
    v_table VARCHAR2_TABLE;
    v_delimpos PLS_INTEGER := INSTR(p_delimstring, p_delim);
    v_delimlen PLS_INTEGER := LENGTH(p_delim);
BEGIN

  WHILE v_delimpos > 0
  LOOP
    v_table(v_nfields) := SUBSTR(v_string,1,v_delimpos-1);
    v_string := SUBSTR(v_string,v_delimpos+v_delimlen);
    v_nfields := v_nfields+1;
    v_delimpos := INSTR(v_string, p_delim);
  END LOOP;

  v_table(v_nfields) := v_string;
  p_table := v_table;
  p_nfields := v_nfields;

END delimstring_to_table;

Reference - Tony Andrews Blog

The functions above are then used inside any relevant SPs within the package:

PROCEDURE Get_Animals(
                   p_Animals          IN       VARCHAR2 := NULL
                   , resultset_out    OUT      resultset_typ  
                   , error_out        OUT      VARCHAR2                                        
                   )
IS

BEGIN

    IF p_Animals IS NULL THEN
    OPEN resultset_out FOR
          SELECT /*+ ALL_ROWS */ * FROM ANIMALS ORDER BY NAME;
    ELSE
        OPEN resultset_out FOR
          SELECT * FROM ANIMALS where NAME in (SELECT * FROM TABLE  (Comma_To_Table(p_RICs)));         
END IF;

error_out := NULL;

EXCEPTION
WHEN OTHERS THEN
           error_out := 'Get_Animals() -> ' || SUBSTR(SQLERRM,1,200);

END Get_Animals; 

If the input string is > 4000 characters, I get the following error from the SP:

ORA-00604: error occurred at recursive SQL level 1
ORA-01003: no statement parsed - GET_ANIMALS

I have two questions:

  1. Can I make the function work with input strings greater than 4000 characters?
  2. Is there a more effective way of achieving the same result?

Any help or suggestions would be much appreciated.

  1. Can I make the function work with input strings greater than 4000 characters? Yes, you can use for example CLOB

  2. Is there a more effective way of achieving the same result? I saw in the comments of the blog a good answer, which is about a recursive solution.

just make some datatype changes for making it to work eg:

  • change the varchar2_table type to CLOB

     TYPE varchar2_table IS TABLE OF CLOB INDEX BY BINARY_INTEGER; 
  • change the VARCHAR2 datatype to CLOB in all p_delimstring occurences

  • change original SUBSTR functions to DBMS_LOB.SUBSTR (if you need more info about that: http://docs.oracle.com/cd/A91202_01/901_doc/appdev.901/a89852/dbms_23b.htm )

     CREATE OR REPLACE PACKAGE parse AS /* || Package of utility procedures for parsing delimited or fixed position strings into tables || of individual values, and vice versa. */ TYPE varchar2_table IS TABLE OF CLOB INDEX BY BINARY_INTEGER; PROCEDURE delimstring_to_table ( p_delimstring IN CLOB , p_table OUT varchar2_table , p_nfields OUT INTEGER , p_delim IN VARCHAR2 DEFAULT ',' ); PROCEDURE table_to_delimstring ( p_table IN varchar2_table , p_delimstring OUT CLOB , p_delim IN VARCHAR2 DEFAULT ',' ); END parse; / CREATE OR REPLACE PACKAGE BODY parse AS PROCEDURE delimstring_to_table ( p_delimstring IN CLOB , p_table OUT varchar2_table , p_nfields OUT INTEGER , p_delim IN VARCHAR2 DEFAULT ',' ) IS v_string CLOB := p_delimstring; v_nfields PLS_INTEGER := 1; v_table varchar2_table; v_delimpos PLS_INTEGER := INSTR(p_delimstring, p_delim); v_delimlen PLS_INTEGER := LENGTH(p_delim); BEGIN WHILE v_delimpos > 0 LOOP v_table(v_nfields) := DBMS_LOB.SUBSTR(v_string,1,v_delimpos-1); v_string := DBMS_LOB.SUBSTR(v_string,v_delimpos+v_delimlen); v_nfields := v_nfields+1; v_delimpos := INSTR(v_string, p_delim); END LOOP; v_table(v_nfields) := v_string; p_table := v_table; p_nfields := v_nfields; END delimstring_to_table; PROCEDURE table_to_delimstring ( p_table IN varchar2_table , p_delimstring OUT CLOB , p_delim IN VARCHAR2 DEFAULT ',' ) IS v_nfields PLS_INTEGER := p_table.COUNT; v_string CLOB; BEGIN FOR i IN 1..v_nfields LOOP v_string := v_string || p_table(i); IF i != v_nfields THEN v_string := v_string || p_delim; END IF; END LOOP; p_delimstring := v_string; END table_to_delimstring; END parse; / 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM