Использование нескольких процедур и нескольких курсоров в пакете - PullRequest
0 голосов
/ 07 июня 2019

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

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

ОЖИДАЕТСЯ

Мне просто нужноудалить многократное выполнение одной и той же записи из процедуры, где я получаю многократное выполнение одной и той же записи

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

Есть ли альтернативный способ решения проблемы?

CREATE OR REPLACE PACKAGE test.report AS
    PROCEDURE distribution (
        code_in   IN             user.test.code%TYPE,
        fromdate      date,
        todate          date
    );

    PROCEDURE tdvalue (
    id   IN          user.test.custid%TYPE
    );

END report;
/

Тело пакета

CREATE OR REPLACE PACKAGE BODY test.report as

----------VARIABLE DECLARATION----------------

    code_in             user.test.code%TYPE;
    custidin                user.test.custid%TYPE;
    fromdate                 DATE;
    todate                   DATE;
    diff                    number(17,2);
---------------CURSOR DECLARATION--------------


CURSOR td_data(code_in  user.test.code%TYPE,
                fromdate date,
                todate date
) IS

    ( SELECT
        test.code,
        COUNT(test.code) AS count,
        SUM(test2.Deposit_amount) AS total,
        test.currency
    FROM
        user.test2
        JOIN user.test ON test2.acid = test.acid
    WHERE
        user.test2.open_effective_date BETWEEN TO_DATE(fromdate, 'dd-mm-yyyy') AND TO_DATE(todate, 'dd-mm-yyyy')
        and
        user.test.code = code_in
    GROUP BY
        test.code,test.currency
    );

    td__data        td_data%rowtype;


CURSOR C_DATA(custidin   user.test.custid%TYPE)  IS   SELECT
            test.custid,
            test2.id,
            TO_DATE(test2.initial_date, 'dd-mm-yyyy') - TO_DATE(test2.end_date, 'dd-mm-yyyy') AS noofdays,
            round(((test2.deposit_amount *((TO_DATE(test2.initial_date, 'dd-mm-yyyy') - TO_DATE(test2.end_date, 'dd-mm-yyyy'
            )) / 365) * test4.interest_rate) / 100), 2) + test2.deposit_amount AS calculated_amount,
            SUM(test.flow_amt) + test2.deposit_amount AS system_amount
        FROM
            user.test
            JOIN user.test2 ON test3.entity_id = test2.id
        WHERE
            test.custid = custidin
        GROUP BY
            test.custid,
            test2.id;

    c__data         c_data%ROWTYPE;


PROCEDURE distribution 
(
    code_in   IN             user.test.code%TYPE,
    fromdate in date,
    todate in  date
)

AS
BEGIN
    OPEN td_data(code_in,fromdate,todate);
    loop
        FETCH td_data INTO td__data;
        dbms_output.put_line(td__data.code
                             || '             '
                             || td__data.count
                             || '                '
                             ||td__data.currency
                             ||' '
                             ||td__data.total
                             );
            end loop;                
    CLOSE td_data;
END distribution;

PROCEDURE tdvalue (
    custidin   IN          user.test.custid%TYPE
    )
AS
BEGIN   
    open c_data(custidin);
    fetch c_data into c__data;
    loop
    diff:= c__data.calculated_amount- c__data.system_amount;        
        dbms_output.put_line(c__data.custid
                             || '   '
                             || c__data.noofdays
                             || '          '
                             || c__data.end_date
                             || '               '
                             || c__data.initial_date
                             || '                 '
                             || c__data.calculated_amount
                             ||'     '
                             ||diff
                             );
    end loop;
    close c_data;
END tdvalue;
END report;
/

Для запуска

ALTER SESSION set nls_date_format='dd-mm-yyyy';
SET SERVEROUTPUT ON;

EXEC REPORT.DISTRIBUTION('872328','01-02-2016','08-02-2019');
/
EXEC REPORT.tdvalue('S9292879383SS53');

Ответы [ 2 ]

1 голос
/ 07 июня 2019

Переполнение буфера - ORU-10027 - происходит, когда общее количество байтов, отображаемых через DBMS_OUTPUT, превышает размер буфера вывода сервера.По умолчанию используется только 20000 байт (кто знает, почему?).Ваш сеанс использует это значение по умолчанию из-за того, что вы включили serveroutput.Очевидно, что одна запись меньше 2000, и вы достигнете этого предела только при запуске нескольких записей.

Чтобы исправить это, попробуйте следующее:

SET SERVEROUTPUT ON size unlimited

Это на самом деле не ограничено, но верхняя границапредел PGA (память сеанса), и вы действительно не должны превышать этот предел с помощью DBMS_OUTPUT.Кроме всего прочего, кто будет читать все это?


Итак, другая проблема с вашим кодом - как указывает @piezol - в том, что у ваших циклов нет точек выхода.Вы должны проверить, действительно ли FETCH что-то выбрал, и выйти, если это не так:

loop
    FETCH td_data INTO td__data;
    exit when td_data%notfound; 

    dbms_output.put_line(td__data.code
                         || '             '
                         || td__data.count
                         || '                '
                         ||td__data.currency
                         ||' '
                         ||td__data.total
                         );
 end loop;  

Запоминание этого - лишь одна из причин, почему неявные курсоры и курсор для циклов предпочтительнее явных курсоров.

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

Итак, давайте перепишем это ...

open c_data(custidin);
fetch c_data into c__data;  -- should be inside 
loop
diff:= c__data.calculated_amount- c__data.system_amount;      

… как курсор для цикла:

PROCEDURE tdvalue (
    custidin   IN          user.test.custid%TYPE
    )
AS
BEGIN   
    for c__data in c_data(custidin)
    loop
        diff:= c__data.calculated_amount- c__data.system_amount;        
        dbms_output.put_line(c__data.custid
                             || '   '
                             || c__data.noofdays
                             || '          '
                             || c__data.end_date
                             || '               '
                             || c__data.initial_date
                             || '                 '
                             || c__data.calculated_amount
                             ||'     '
                             ||diff
                             );
    end loop;
END tdvalue;

Нет необходимости в OPEN, CLOSE или FETCH, и нет необходимостипроверить, когда курсор исчерпан.

0 голосов
/ 07 июня 2019

В PL / SQL предпочтительный механизм установки размера буфера DBMS_OUTPUT будет в вашей процедуре. Это дает преимущество работы с любым клиентским инструментом, таким как Java или Toad (хотя клиентский инструмент все еще должен извлекать выходные данные из DBMS_OUTPUT).

DBMS_Output.ENABLE

Передать параметр NULL для неограниченного размера буфера.

Это будет выглядеть так:

BEGIN
  DBMS_OUTPUT.ENABLE(NULL);
  FOR I IN 1..1000 LOOP
    DBMS_OUTPUT.PUT_LINE('The quick red fox jumps over the lazy brown dog.');
  END LOOP;
END;
/

Факт бонуса:

Вы можете использовать другие функции и процедуры в DBMS_OUTPUT, чтобы прокрутить свои собственные, если вы не используете SQL * Plus или инструмент DBMS_OUTPUT -svvy, такой как Toad. Вы можете использовать процедуры GET_LINE или GET_LINES из своего клиентского кода, чтобы получить все, что может быть записано в DBMS_OUTPUT.

GET_LINE

...