以CLOB作为输入参数并拆分字符串的Oracle函数

zengzsys  于 2023-04-11  发布在  Oracle
关注(0)|答案(1)|浏览(215)

我创建了一个Oracle表函数,它可以在表中删除和插入传递的值。
是的,我知道函数不应该这样做,我必须使用存储过程,但不幸的是,我不能在另一个工具中使用存储过程,所以我误用了这个函数,并添加了PRAGMA,这样这个函数就可以在函数体中使用COMMIT了。这个函数可以按照我想要的方式工作,但是字符串文字大于4000个字符时有一个问题。我认为CLOBDBMS_CLOB函数可以处理大字符串,但我仍然得到错误
ORA-01704:字符串文字太长
01704. 00000 -“字符串文字太长”

  • 原因:字符串文字长度超过4000个字符。
  • 操作:使用最多4000个字符的字符串文字。
    较长的值只能使用绑定变量输入。
    这是因为我传递的字符串非常大,超过了4000个字符,输入参数p_val定义为CLOB并不重要,传递字符串时4000个字符总是限制?
create or replace FUNCTION table_fn_test (p_val CLOB, p_action VARCHAR2, p_random NUMBER, p_user VARCHAR2)
RETURN my_t_tf_tab PIPELINED
IS
    PRAGMA AUTONOMOUS_TRANSACTION;
    v_count NUMBER :=0;
    v_dup NUMBER :=0;
    v_info  VARCHAR2(50) :='';
    v_val CLOB;
    v_index NUMBER := 1;
BEGIN

    IF p_val IS NOT NULL THEN
        v_val := p_val || ',';
        WHILE DBMS_LOB.INSTR(v_val, ',', v_index) > 0
        LOOP
            IF p_action = 'insert' THEN
                SELECT COUNT(*) INTO v_dup FROM my_table WHERE COL2 = TRIM(DBMS_LOB.SUBSTR(v_val,DBMS_LOB.INSTR(v_val,'#',v_index)-v_index,v_index)) AND COL1 =  RPAD(' ' || TRIM(DBMS_LOB.SUBSTR( v_val,DBMS_LOB.INSTR(v_val,',',v_index)-DBMS_LOB.INSTR(v_val,'#',v_index)-1,DBMS_LOB.INSTR(v_val,'#',v_index)+1)),15,' ');
                IF v_dup = 0 THEN
                    INSERT INTO my_table(COL2,COL1) VALUES (TRIM(DBMS_LOB.SUBSTR(v_val,DBMS_LOB.INSTR(v_val,'#',v_index)-v_index,v_index)), RPAD(' ' || TRIM(DBMS_LOB.SUBSTR( v_val,DBMS_LOB.INSTR(v_val,',',v_index)-DBMS_LOB.INSTR(v_val,'#',v_index)-1,DBMS_LOB.INSTR(v_val,'#',v_index)+1)),15,' '));
                    v_count := v_count + SQL%ROWCOUNT;
                    v_info := v_count || ' row(s) inserted';
                END IF;
            ELSIF p_action = 'delete' THEN
                DELETE FROM my_table WHERE COL2 = TRIM(DBMS_LOB.SUBSTR(v_val,DBMS_LOB.INSTR(v_val,'#',v_index)-v_index,v_index)) AND COL1 =  RPAD(' ' || TRIM(DBMS_LOB.SUBSTR( v_val,DBMS_LOB.INSTR(v_val,',',v_index)-DBMS_LOB.INSTR(v_val,'#',v_index)-1,DBMS_LOB.INSTR(v_val,'#',v_index)+1)),15,' ');
                v_count := v_count + SQL%ROWCOUNT;
                v_info := v_count || ' row(s) deleted';
            END IF;
            v_index := DBMS_LOB.INSTR(v_val, ',', v_index) + 1;
        END LOOP;
        COMMIT;
    END IF;

    FOR rec IN (SELECT COL1, COL2 from my_table )
    LOOP
        PIPE ROW(my_t_tf_tab(rec.COL2, rec.COL1 ));
    END LOOP;
END table_fn_test;
xdyibdwo

xdyibdwo1#

当传递文字时,4000个字符始终是限制?
是的,除非你的数据库配置了更大的32 k限制(从技术上讲,它是字节,而不是字符,这可能是这里的问题)。
问题出在你的调用上,而不是函数本身。你需要构造一个CLOB来传入--如何传递取决于你在哪里/如何调用它。
在SQL中,你可以将几个较短的字符串连接在一起:

select table_fn_test (
  to_clob('less that 4000')
  || to_clob('less than 4000')
  || to_clob('less than 4000') /* etc */,
  ...)
from dual

如果你有一个很大的字符串作为开始,这显然有点痛苦,因为你必须在复制和粘贴它时将其分解。如果值在文件中或从应用程序传递,那么你应该尽早读取并将其视为CLOB,所以它永远不会受到这个限制。

相关问题