在 Oracle (> 11g) 中从逗号分隔列表创建表 - 输入字符串限制 4000 个字符

Posted

技术标签:

【中文标题】在 Oracle (> 11g) 中从逗号分隔列表创建表 - 输入字符串限制 4000 个字符【英文标题】:Creating a table from a Comma Separated List in Oracle (> 11g) - Input string limit 4000 chars 【发布时间】: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

我有两个问题:

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

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

【问题讨论】:

您能提供所需的输出吗?谢谢 【参考方案1】:

    我能否使函数处理大于 4000 个字符的输入字符串? 是的,您可以使用例如 CLOB

    有没有更有效的方法可以达到同样的效果? 我在博客的 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 表的路线。

以上是关于在 Oracle (> 11g) 中从逗号分隔列表创建表 - 输入字符串限制 4000 个字符的主要内容,如果未能解决你的问题,请参考以下文章

在 oracle 中处理逗号分隔值的好方法

如何计算oracle中从一个字符到另一个字符的子字符串的长度

Oracle 11g expdp中query参数的使用

Oracle11g 带分页的选择查询

如何实现Oracle 11g数据库每天自动备份

Oracle11g的服务