在 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 个字符的主要内容,如果未能解决你的问题,请参考以下文章