Слишком маленький буфер символов - PullRequest
0 голосов
/ 24 апреля 2019

У меня возникла проблема при конкатенации типа данных varchar2 в цикле курсора.

Процедура выполняет итерацию в цикле для построения предложения in для операций вставки и удаления в пакете. Процесс будет запускаться в пакете длякаждые 1000 номеров учетных записей.

Для небольшого количества записей это работает, но когда он пытается объединить большое количество записей (36451477 в временной таблице) в цикле, он выбрасывает.

java.sql.SQLException: ORA-06502: PL / SQL: ошибка числового значения или значения: слишком маленький буфер строки символов ORA-06512: в «QA01BT.LOAD_ITEM_DATA_TO_CONSOLIDATE», строка 23 ORA-06512: в строке 1

Я установил максимальный предел поискового идентификатора до 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 голосов
/ 24 апреля 2019

Если вы решили, что вам нужно сделать это в пакетном режиме, и беспокоитесь о том, что строка слишком длинная или слишком много элементов в списке (максимум 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;
0 голосов
/ 24 апреля 2019

Это выглядит несколько перегруженным. Почему не только это?

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;
...