字符串缓冲区太小ORA-06502

时间:2019-04-24 15:35:38

标签: sql oracle plsql query-optimization

在游标循环中串联varchar2数据类型时遇到问题。

过程在循环中迭代以构建in子句以批量执行插入和删除操作。该过程将针对每1000个帐号批量运行。

对于少量记录,它可以工作,但是当尝试在循环中连接大量记录(临时表中的36451477)时,它会抛出。

  

java.sql.SQLException:ORA-06502:PL / SQL:数字或值错误:   字符串缓冲区太小ORA-06512:在   “ QA01BT.LOAD_ITEM_DATA_TO_CONSOLIDATE”,第23行ORA-06512:在第1行

我已将搜索ID的最大值限制为32767,但仍然无法正常工作。

还有其他方法可以实现吗?

create or replace PROCEDURE LOAD_ITEM_DATA_TO_CONSOLIDATE(updatecount OUT NUMBER
)
IS
  cnt       NUMBER := 0;
  c_limit CONSTANT PLS_INTEGER DEFAULT 1000;
  search_id varchar2(32727);
  TYPE account_array
    IS TABLE OF VARCHAR2(255) INDEX BY BINARY_INTEGER;
  l_data    ACCOUNT_ARRAY;
  CURSOR account_cursor IS
    SELECT DISTINCT account_no AS account_num
    FROM   item_temp;
BEGIN
    OPEN account_cursor;

    LOOP
        FETCH account_cursor bulk collect INTO l_data limit c_limit;

        search_id := '''';

        FOR i IN 1 .. l_data.count LOOP
            IF( i != 1 ) THEN
              search_id := search_id
                           || ','
                           || ''''
                           || l_data(i)
                           || '''';
            ELSE
              search_id := search_id
                           || l_data(i)
                           || '''';
            END IF;
        END LOOP;

        BEGIN

        SAVEPOINT move_data_to_temp_table;

        EXECUTE IMMEDIATE 'delete from item where ACCOUNT_NO IN('||search_id||')';

        EXECUTE IMMEDIATE 'insert into item(ID,ACCOUNT_NO,ITEM_ID,ITEM_VALUE) select HIBERNATE_SEQUENCE.nextval,temp.ACCOUNT_NO,temp.ITEM_ID,temp.ITEM_VALUE from item_TEMP temp     where ACCOUNT_NO IN('||search_id||')';

        cnt := cnt + SQL%rowcount;

        COMMIT;

        EXCEPTION WHEN OTHERS THEN ROLLBACK to move_data_to_temp_table;

        END;

        EXIT WHEN account_cursor%NOTFOUND;

    END LOOP;

    updatecount := cnt;

    CLOSE account_cursor;

END LOAD_ITEM_DATA_TO_CONSOLIDATE;

2 个答案:

答案 0 :(得分:0)

这似乎有些过分设计。为什么不只是这个?

create or replace PROCEDURE LOAD_ITEM_DATA_TO_CONSOLIDATE
    (updatecount OUT NUMBER)
IS
BEGIN
    delete from item 
    where ACCOUNT_NO IN ( SELECT account_no
                          FROM   item_temp);

   insert into item(ID,ACCOUNT_NO,ITEM_ID,ITEM_VALUE) 
   select HIBERNATE_SEQUENCE.nextval, temp.ACCOUNT_NO, temp.ITEM_ID, temp.ITEM_VALUE 
   from item_TEMP temp  ;

    updatecount := SQL%rowcount;

END LOAD_ITEM_DATA_TO_CONSOLIDATE;

答案 1 :(得分:0)

如果您确定需要分批执行此操作,并且担心该字符串太长或列表中的元素过多(最大为1000),则应尝试将值放入数组,然后使用IN通过表函数或对表的直接引用对数组进行操作。

额外好处:无需动态SQL!

类似这样的东西:

CREATE OR REPLACE TYPE strings_t IS TABLE OF VARCHAR2 (255)
/

CREATE OR REPLACE PROCEDURE load_item_data_to_consolidate (
   updatecount   OUT NUMBER)
IS
   cnt                NUMBER := 0;
   c_limit   CONSTANT PLS_INTEGER DEFAULT 1000;
   l_data             strings_t;

   CURSOR account_cursor
   IS
      SELECT DISTINCT account_no AS account_num FROM item_temp;
BEGIN
   OPEN account_cursor;

   LOOP
      FETCH account_cursor BULK COLLECT INTO l_data LIMIT c_limit;

      BEGIN
         SAVEPOINT move_data_to_temp_table;

         DELETE FROM item
               WHERE account_no IN (SELECT COLUMN_VALUE FROM TABLE (l_data));

         INSERT INTO item (id,
                           account_no,
                           item_id,
                           item_value)
            SELECT hibernate_sequence.NEXTVAL,
                   temp.account_no,
                   temp.item_id,
                   temp.item_value
              FROM item_temp temp
             WHERE account_no IN (SELECT COLUMN_VALUE FROM TABLE (l_data));

         cnt := cnt + SQL%ROWCOUNT;

         COMMIT;
      EXCEPTION
         WHEN OTHERS
         THEN
            ROLLBACK TO move_data_to_temp_table;
      END;

      EXIT WHEN account_cursor%NOTFOUND;
   END LOOP;
END;