【问题标题】:using long string(over 4000) in oracle query在 oracle 查询中使用长字符串(超过 4000 个)
【发布时间】:2011-09-06 18:45:00
【问题描述】:

我知道在sql中varchar2只能是4000左右。

我知道在 oracle PL varchcar2 可以在 32000 左右。

我定义了一个长度超过 4000 个字符的 varchar2 变量,我想在查询中使用它。我不想将值插入表中。该值是一个分隔字符串,我使用此查询解析并插入到表中。当变量长度少于 4000 个字符时,此查询有效。有没有办法让它最多支持 32000 个字符?

create global temporary table t(single_element varchar(500),element_no number);
declare
--declared as 32767 but this string contains less than 4000 characters. 
--This will work. If you expand the string to 32000 characters it will not work.
myvar varchar2(32767) := 'tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4^~tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testmsg4';
begin
delete from t;
insert into t
SELECT SUBSTR(str, start_pos, (next_pos-start_pos)) AS single_element, element_no
FROM  (
        SELECT 
              ilv.str, 
              nt.column_value AS element_no, 
              INSTR(ilv.str, '^~', DECODE(nt.column_value, 1, 0, 1), DECODE(nt.column_value, 1, 1, nt.column_value-1)) + 2 AS start_pos,
              INSTR(ilv.str, '^~', 1, DECODE(nt.column_value, 1, 1, nt.column_value)) AS next_pos
        FROM   (
                select '~' || myvar || '^~' as str, 
                (Length(myvar) - length(replace(myvar,'^~','')))/2 + 2 as no_of_elements 
                from dual) ilv,

              TABLE(
                    CAST(
                       MULTISET(
                          SELECT ROWNUM FROM dual CONNECT BY ROWNUM < ilv.no_of_elements
                          ) AS number_ntt )) nt
         );
end;

将“myvar”扩展为 32000 个字符时出现的错误是

can bind a LONG value only for insert into a LONG column

有没有办法绕过这个大小限制,因为我实际上并没有将此值插入到表中,我只是在查询中使用它?

【问题讨论】:

    标签: oracle plsql varchar2


    【解决方案1】:

    您必须将变量定义为 VARCHAR2 吗?你能把它定义为 CLOB 吗?

    如果我将 MYVAR 的声明从 VARCHAR2(32767) 更改为 CLOB 并定义 NUMBER_NTT 类型,您的代码将为我运行

    SQL> ed
    Wrote file afiedt.buf
    
    SP2-0161: line 2 truncated.
      1  declare
      2  myvar clob := 'tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3^~tcd4~#testms
      <<snip>>
    ~tcd3~#testmsg3^~tcd4~#testmsg4';
      4  begin
      5  delete from t;
      6  insert into t
      7  SELECT SUBSTR(str, start_pos, (next_pos-start_pos)) AS single_element, elem
    ent_no
      8  FROM  (
      9          SELECT
     10                ilv.str,
     11                nt.column_value AS element_no,
     12                INSTR(ilv.str, '^~', DECODE(nt.column_value, 1, 0, 1), DECODE
    (nt.column_value, 1, 1, nt.column_value-1)) + 2 AS start_pos,
     13                INSTR(ilv.str, '^~', 1, DECODE(nt.column_value, 1, 1, nt.colu
    mn_value)) AS next_pos
     14          FROM   (
     15                  select '~' || myvar || '^~' as str,
     16                  (Length(myvar) - length(replace(myvar,'^~','')))/2 + 2 as n
    o_of_elements
     17                  from dual) ilv,
     18                TABLE(
     19                      CAST(
     20                         MULTISET(
     21                            SELECT ROWNUM FROM dual CONNECT BY ROWNUM < ilv.n
    o_of_elements
     22                            ) AS number_ntt )) nt
     23           );
     24* end;
     25  /
    
    PL/SQL procedure successfully completed.
    
    SQL> select count(*) from t;
    
      COUNT(*)
    ----------
           172
    

    话虽如此,这不是我解析分隔字符串的方式,尤其是在 PL/SQL 中。但它确实有效。

    【讨论】:

    • 我试图想出一种比循环更有效的方法来解析字符串。这似乎比遍历分隔字符串并单独插入每一行要快。您对如何解析字符串有其他想法吗?
    • 附注- 我对非基于集合的操作有偏见...... dbms 本质上是以基于集合的方式运行的,我还没有看到一个例子,在基于集合的解决方案中使用循环具有自然优势。跨度>
    【解决方案2】:

    好的,这很接近你的实现偏差的边缘,虽然记住 forall 是一个批量绑定操作,而不是一个真正的循环,但是你看过 dbms_utility.comma_to_table 函数吗?

    这是一个优化的内部 oracle 解析函数,尽管有一些限制,您可以在此处阅读:http://www.techiegyan.com/2009/02/17/oracle-breaking-comma-separated-string-using-dbms_utilitycomma_to_table/

    如果您解析的字段以数字、特殊字符、包含逗号等开头,则需要 replace() 使其以逗号分隔,并且还需要双引号括起来

    但如果您的数据允许 - 它肯定会让您的代码看起来更干净(并且可能会更快地运行)

    declare
       myvar      varchar2(32000) := 'tcd1~#testmsg1^~tcd2~#testmsg2^~tcd3~#testmsg3';
       mycnt      binary_integer;
       myresults  sys.dbms_utility.lname_array;
    begin
       sys.dbms_utility.comma_to_table('"'||replace(myvar,'^~','","')||'"', mycnt, myresults );
       delete from t;
       forall ix in myresults.first..myresults.last 
          insert into tvalues (myresults(ix));
       commit;
    end;
    

    【讨论】:

    • 想到了这个,但是如果他们使用两个特殊字符作为分隔符,我猜是因为他们不能保证数据不包含逗号......
    猜你喜欢
    • 1970-01-01
    • 2012-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-13
    相关资源
    最近更新 更多