ORA-06502: PL/SQL: числовая ошибка или ошибка значения: буфер символьной строки слишком мал для агрегатных функций Oracle

Гуру добрый день,

У меня есть скрипт, который регулярно заполняет таблицы, который дал сбой и выдал указанную выше ошибку. Странно то, что он работал в производственной системе почти 3 месяца без проблем и внезапно рухнул на прошлой неделе. Насколько мне известно, никаких изменений в таблицах не было.

Кто-нибудь сталкивался с чем-то подобным раньше? Я считаю, что это как-то связано с агрегатными функциями, которые я в нем реализую; но это работало изначально.

пожалуйста; любезно прикрепите часть сценария, которую я превратил в процедуру, которая, как я полагаю, дает ошибку.

СОЗДАЙТЕ ИЛИ ЗАМЕНИТЕ ПРОЦЕДУРУ V1 IS

--ОБЪЯВИТЬ

    v_a       VARCHAR2(4000);
    v_b       VARCHAR2(4000);
    v_c       VARCHAR2(4000);
    v_d       VARCHAR2(4000);
    v_e       VARCHAR2(4000);
    v_f       VARCHAR2(4000);
    v_g       VARCHAR2(4000);
    v_h       VARCHAR2(4000);
    v_i       VARCHAR2(4000);
    v_j       VARCHAR2(4000);
    v_k       VARCHAR2(4000);
    v_l       VARCHAR2(4000);
    v_m       VARCHAR2(4000);
    v_n       NUMBER(10);
    v_o       VARCHAR2(4000);

--
-- Процедура заполнения DEMO-таблицы

        BEGIN

              -- Delete all from the DEMO table
              DELETE FROM DEMO;

              -- Populate fields in DEMO from DEMOV1
              INSERT INTO DEMO(ID, D_ID, CTR_ID, C_ID, DT_NAM, TP, BYR, ENY,
                               ONG, SUMM, DTW, REV, LD, MD, STAT, CRD)
              SELECT ID, D_ID, CTR_ID, C_ID, DT_NAM, TP, TO_NUMBER(TO_CHAR(BYR,'YYYY')), 
                   TO_NUMBER(TO_CHAR(NVL(ENY,SYSDATE),'YYYY')), CASE WHEN ENY IS NULL THEN 'Y' ELSE 'N' END, SUMMARY, DTW,
                   REV, LD, MD, '1', SYSDATE 
              FROM DEMOV1;

          -- LOOP THROUGH DEMO TABLE
          FOR j IN (SELECT ID, CTR_ID, C_ID FROM DEMO)
          LOOP




                Select semic_concat(TXTDESC)
                INTO v_a 
                From GEOT
                WHERE ID = j.ID;



               SELECT COUNT(*)
               INTO v_n
               FROM MERP M, PROJ P
               WHERE M.MID = P.COD
               AND ID = j.ID
               AND PROAC IS NULL;

               IF (v_n > 0)
               THEN


                    Select semic_concat(PRO)
                    INTO v_b 
                    FROM MERP M, PROJ P
                    WHERE M.MID = P.COD
                    AND ID = j.ID;

               ELSE


                    Select semic_concat(PRO || '(' || PROAC || ')' )
                    INTO v_b 
                    FROM MERP M, PROJ P
                    WHERE M.MID = P.COD
                    AND ID = j.ID;

               END IF;


                Select semic_concat(VOCNAME('P02',COD))
                INTO v_c 
                From PAR
                WHERE ID = j.ID;



                Select semic_concat(VOCNAME('L05',COD))
                INTO v_d 
                From INST
                WHERE ID = j.ID;


                Select semic_concat(NVL(AUTHOR,'Anon') ||' ('||to_char(PUB,'YYYY')||') '||TITLE||', '||EDT)
                INTO v_e 
                From REFE
                WHERE ID = j.ID;



                Select semic_concat(NAM)
                INTO v_f 
                FROM EDM E, EDO EO
                WHERE E.EDMID = EO.EDOID
                AND ID = j.ID;



                Select semic_concat(VOCNAME('L08', COD))
                INTO v_g 
                FROM AVA 
                WHERE ID = j.ID;



               SELECT or_concat(NAM)
               INTO v_o
               FROM CON 
               WHERE ID = j.ID
               AND NAM = 'Unknown';

                    IF (v_o = 'Unknown')
                    THEN

                        Select or_concat(JOBTITLE || ' (' || EMAIL || ')')
                        INTO v_h 
                        FROM CON 
                        WHERE ID = j.ID;

                    ELSE

                        Select or_concat(NAM || ' (' || EMAIL || ')')
                        INTO v_h 
                        FROM CON 
                        WHERE ID = j.ID;

                    END IF;



                Select commaencap_concat(COD)
                INTO v_i 
                FROM PAR 
                WHERE ID = j.ID;

                IF (v_i = ',')
                THEN

                    v_i := null;

                ELSE

                    Select commaencap_concat(COD)
                    INTO v_i 
                    FROM PAR 
                    WHERE ID = j.ID;


                END IF;



                Select commaencap_concat(COD)
                INTO v_j 
                FROM INST 
                WHERE ID = j.ID;

                IF (v_j = ',')
                THEN

                    v_j := null;

                ELSE

                    Select commaencap_concat(COD)
                    INTO v_j 
                    FROM INST
                    WHERE ID = j.ID;

                END IF;



                Select commaencap_concat(COD)
                    INTO v_k 
                    FROM SAR 
                    WHERE ID = j.ID;

                IF (v_k = ',')
                THEN

                    v_k := null;

                ELSE

                    Select commaencap_concat(COD)
                    INTO v_k 
                    FROM SAR 
                    WHERE ID = j.ID;

                END IF;




                Select commaencap_concat(CONID)
                    INTO v_l 
                    FROM CON 
                    WHERE ID = j.ID;

                IF (v_l = ',')
                THEN

                    v_l := null;

                ELSE

                    Select commaencap_concat(CONID)
                    INTO v_l 
                    FROM CON 
                    WHERE ID = j.ID;

                END IF;



                Select commaencap_concat(PROID)
                    INTO v_m 
                    FROM PRO 
                    WHERE ID = j.ID;

                IF (v_m = ',')
                THEN

                    v_m := null;

                ELSE

                    Select commaencap_concat(PROID)
                    INTO v_m 
                    FROM PRO 
                    WHERE ID = j.ID;

                END IF;

                 -- UPDATE DEMO TABLE
                 UPDATE DEMO
                 SET GEOC = v_a,
                     PRO = v_b,
                     PAR = v_c,
                     INS = v_d,
                     REFER = v_e,
                     ORGR = v_f,
                     AVAY = v_g,
                     CON = v_h,
                     DTH = v_i,
                     INST = v_j,
                     SA = v_k,
                     CC = v_l,
                     EDPR = v_m,
                     CTR = (SELECT NAM
                                  FROM EDM
                                  WHERE EDMID = j.CTR_ID),
                     COLL = (SELECT NAM
                                    FROM EDM
                                    WHERE EDMID = j.C_ID)

                WHERE ID = j.ID;

          END LOOP;


        END V1;

/

Агрегатные функции, commaencap_concat (инкапсулирует запятую), or_concat (конкатирует с помощью or) и semic_concat (конкатирует с точкой с запятой).

остальные используемые таблицы связаны с основной таблицей DEMO.

Я проверил размеры столбцов, и, похоже, проблем нет. Я попытался выполнить только операторы SELECT, и они дают ту же ошибку без заполнения таблиц.

Любые подсказки?

Большое спасибо за ожидаемую поддержку.

Спасибо APC за вашу помощь; в частности, тип строки и рекомендации по отладке. Я проверил все столбцы друг против друга и сделал те, которые были меньше 4000 байт, размером до 4000 байт, но я все еще получаю то же сообщение об ошибке.

Оператор SELECT, который я попробовал, был с одной из агрегатных функций, которые находятся в процедуре:

            Select semic_concat(TXTDESC)
           -- INTO v_a 
            From GEOT
            WHERE ID IN (SELECT ID FROM DEMO);

и это вызвало ту же ошибку слишком маленького строкового буфера; но когда я добавил группу к оператору, это сработало.

            Select semic_concat(TXTDESC)
            -- INTO v_a 
            From GEOT
            WHERE ID IN (SELECT ID FROM DEMO)
            GROUP BY ID;

Я попытался поменять местами все вхождения таких операторов в процедуре, но все равно выдает ту же ошибку. Еще раз спасибо; все еще работаю над этим.


person Tunde    schedule 09.06.2010    source источник
comment
Было бы очень полезно, если бы вы дали нам какую-нибудь подсказку, например пример кода. Также версия базы данных. И, возможно, некоторый контекст (что делает ваше приложение).   -  person APC    schedule 09.06.2010
comment
АПК прав. Без примера кода мы не сможем провести проверку кода, чтобы помочь вам, Тунде. Конечно, вы морально обязаны зашифровать все значащие имена переменных, чтобы мы не догадались, где вы работаете или чем вы интересуетесь, по очевидным причинам конфиденциальности и соблюдения политики безопасности вашей фирмы.   -  person UltraCommit    schedule 09.06.2010
comment
@APC, спасибо. @ Курица на кухне, я зашифровал все, что мог. Странно то, что операторы SQL сами по себе перестали работать с агрегатными функциями, будучи изолированными от процедуры. Спасибо.   -  person Tunde    schedule 09.06.2010


Ответы (1)


Агрегирующие функции, э-э, агрегат. Это означает, что они добавляют числа или объединяют строки вместе. Чем больше исходные значения, тем больше агрегированный продукт.

Не зная более подробной информации, трудно быть уверенным, но наиболее вероятное объяснение состоит в том, что что-то в ваших данных изменилось, в результате чего ваше агрегированное значение теперь слишком велико, чтобы поместиться в целевой столбец.

изменить

"Я попробовал выполнить оператор SELECT сам по себе и выдает ту же ошибку..."

Хм, какой ВЫБРАТЬ? В вашей процедуре их полно.

В любом случае, вам нужно научиться отлаживать свой код. (1)

Лучший способ отлаживать PL/SQL — использовать IDE, которая поддерживает такие возможности. Это делают как TOAD, так и PL/SQL Developer, а также собственный (бесплатный) продукт Oracle SQL Developer. Подробнее.

В качестве альтернативы вы можете использовать DBMS_OUTPUT (также известный как отладчик дьявола) и интерполировать множество вызовов DBMS_OUTPUT.PUT_LINE(), чтобы увидеть, какой оператор вы собираетесь выполнить, и длину соответствующих значений.

Однако, учитывая, что все ваши строковые переменные PL/SQL имеют максимальную длину столбца SQL — varchar2(4000) — я бы сосредоточился на коде, который заполняет вашу переменную v_n. number(10) ни в коем случае не является самым большим числом, которое можно хранить в SQL, так как вы нарушаете ограничение буфера, которое может показаться наиболее вероятным кандидатом. Хотя, поскольку в сообщении об ошибке упоминается буфер string, я могу продать вам бездельника.

Другая возможность - это окончательный оператор UPDATE. Все столбцы DEMO имеют размер varchar2(4000)? Если нет, то вам нужно посмотреть на них. Рекомендуется указывать переменные с использованием синтаксиса %TYPE:

 v_a demo.geoc%TYPE;
 v_b demo.pro%TYPE;

Или, чтобы не печатать, укажите одну переменную записи:

v_demo demo%rowtype;

На что вы можете ссылаться следующим образом:

select semic_concat(TXTDESC) 
into v_demo.geoc  
From GEOT 
WHERE ID = j.ID; 

(Кстати, можно использовать переменную уровня строки в операторах обновления с использованием синтаксиса UPDATE ... SET ROW =, но я не думаю, что это будет уместно в вашей ситуации.)

изменить 2

Опять же, NO_DATA_FOUND указывает на проблему с данными. Если наша база данных не предназначена только для чтения, мы должны ожидать, что данные изменятся, и мы должны обрабатывать исключения, связанные с данными. Если причина, по которой вы не обрабатываете NO_DATA_FOUND, заключается в том, что данные всегда должны быть там, у вас есть более серьезная проблема, возможно, отсутствующий или отключенный внешний ключ. Как правило, безопаснее предположить, что мы получим NO_DATA_FOUND, TOO_MANY_ROWS и т. д. и включим полезные обработчики исключений для регистрации соответствующих деталей.


сноска (1) Или научитесь разрабатывать его. Сначала протестируйте его, используя модульное тестирование, но это сценарий стабильной двери, коня нет...

person APC    schedule 09.06.2010
comment
Проголосовал за отладчик дьявола - person Gary Myers; 10.06.2010
comment
APC, еще раз большое спасибо за вашу помощь и содействие. Теперь я получаю другое сообщение об ошибке NO DATA FOUND с одним из операторов выбора, включающих агрегатные функции. Странно то, что он проработал почти 3 месяца... все еще работаю над ним. Еще раз спасибо; Я очень ценю ваши усилия. - person Tunde; 10.06.2010
comment
Большое спасибо APC, я добавил исключение NO_DATA_FOUND, и оно перестало падать, но не выполняло то, для чего предназначалось, то есть заполнило таблицу DEMO; но я сомневаюсь, что это проблема. Каждый оператор SQL возвращает значение при отдельном запуске вне блока PL/SQL; т. е. Select semic_concat(TXTDESC) -- INTO v_a From GEOT WHERE ID IN (SELECT ID FROM DEMO) GROUP BY ID; Еще раз спасибо. - person Tunde; 14.06.2010
comment
Большое спасибо APC, я смог это исправить. На самом деле это было одно из агрегированных значений, которое превышало 4000 байт и было слишком большим как для функции агрегирования, так и для столбца назначения. Это увеличилось в размере из-за того, что в исходную таблицу было добавлено больше данных. Еще раз большое спасибо за вашу помощь, помощь и советы. Ваше здоровье - person Tunde; 14.06.2010