【问题标题】:Creating a table from a Comma Separated List in Oracle (> 11g) - Input string limit 4000 chars在 Oracle (> 11g) 中从逗号分隔列表创建表 - 输入字符串限制 4000 个字符
【发布时间】:2014-08-27 08:13:50
【问题描述】:

我有一个 Oracle (11g) SP,它采用逗号分隔的输入字符串(例如“猫、狗、猴子”)。代码的症结取自 Tony Andrews 的博客,按预期工作,但不接受超过 4000 个字符的输入字符串:

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

然后在包内的任何相关 SP 中使用上述函数:

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; 

如果输入字符串大于 4000 个字符,我会从 SP 收到以下错误:

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

我有两个问题:

  1. 我可以让函数处理超过 4000 个字符的输入字符串吗?
  2. 有没有更有效的方法可以达到同样的效果?

任何帮助或建议将不胜感激。

【问题讨论】:

  • 您能提供所需的输出吗?谢谢

标签: sql oracle oracle11g


【解决方案1】:
  1. 我能否使函数处理大于 4000 个字符的输入字符串? 是的,您可以使用例如 CLOB

  2. 有没有更有效的方法可以达到同样的效果? 我在博客的 cmets 中看到了一个很好的答案,这是关于递归解决方案的。

只需进行一些数据类型更改以使其正常工作,例如:

  • 将 varchar2_table 类型更改为 CLOB

    TYPE varchar2_table IS TABLE OF CLOB INDEX BY BINARY_INTEGER;
    
  • 在所有 p_delimstring 出现中将 VARCHAR2 数据类型更改为 CLOB

  • 将原来的 SUBSTR 函数更改为 DBMS_LOB.SUBSTR (如果您需要更多相关信息: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;
    /
    

【讨论】:

  • 您能否详细说明您将如何使用 CLOB 来执行此操作?
  • 感谢 Gabor - 实验过,看起来很慢。也许将不得不走使用 Global Temp 表的路线。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多