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

huangapple go评论59阅读模式
英文:

Oracle function with CLOB as input parameter and spliting the string

问题

我创建了一个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 || ' 行已插入';
                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 || ' 行已删除';
            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;
英文:

I created an Oracle table function that can delete and insert passed values into my table.

Yes I know that function shouldn't do that and I have to use stored procedure but unfortunately I can not use the stored procedure in another tool, so I misuse the function and add PRAGMA so the the function can use COMMIT in the body as well. The function works as I want but there is a issue with string literal bigger than 4000 characters. I think CLOB and the DBMS_CLOB function can handle large strings but I still get an error

> ORA-01704: string literal too long
> 01704. 00000 - "string literal too long"
> *Cause: The string literal is longer than 4000 characters.
> *Action: Use a string literal of at most 4000 characters.
> Longer values may only be entered using bind variables.

It is because I am passing very large string literal that exceeds 4000 characters and it doesn't matter that the input parameter p_val is defined as CLOB, 4000 characters is always the limit when passing literals?

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;

答案1

得分: 1

4000 characters is always the limit when passing literals?

是的,除非您的数据库已配置为更大的 32k 限制。 (严格来说,这是字节而不是字符,这可能在这里很重要。)

问题出在您的调用,而不是函数本身。您需要构建一个 CLOB 以进行传递,具体方式取决于您在何处以及如何调用它。

从 SQL 中,您可以将几个较短的字符串连接在一起:

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

如果您的初始字符串很大,那显然有点麻烦,因为您必须在复制和粘贴时将其分割。如果该值存储在文件中或从应用程序传递,则应尽早将其读取并处理为 CLOB,以便不受此限制的限制。

英文:

> 4000 characters is always the limit when passing literals?

Yes, unless your DB is configured for the larger 32k limit. (And technically it's bytes, not characters, which might be matter here.)

The problem is with your call, not with the function. You will need to construct a CLOB to pass in - how depends on where/how you call it.

From SQL you could concatenate several shorter strings together:

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

That's obviously a bit painful if you have a huge string to start with as you have to break it up as you copy and paste it. If the value is in a file or passed from an application then you should read and treat it as a CLOB as early as you can, so it's never subject to that limit.

huangapple
  • 本文由 发表于 2023年4月4日 16:35:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/75927196.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定