Пакетная процедура, вызывающая частную функцию - непригодна для использования в SQL - PullRequest
0 голосов
/ 06 мая 2020

У меня есть пакет с двумя частными функциями и одной процедурой, которая их вызывает. Эти две функции возвращают в процедуру сроки (первая и последняя) объявленных платежей. Процедура возвращает эти даты вместе с именем и идентификатором донора.

CREATE OR REPLACE PACKAGE PLEDGE_PKG IS
 PROCEDURE DD_PLIST_PP
    (p_id IN DD_PLEDGE.IDPLEDGE%TYPE,
     p_first DD_DONOR.FIRSTNAME%TYPE,
     p_last DD_DONOR.LASTNAME%TYPE,
     p_payfirst OUT DATE,
     p_paylast OUT DATE);
END;
CREATE OR REPLACE PACKAGE BODY PLEDGE_PKG IS
-- Determines 1st Payment Due Date based on ID.
 FUNCTION dd_paydate1_pf
  (p_id IN dd_pledge.idpledge%TYPE)
  RETURN DATE
  IS
  lv_pl_dat DATE;
  lv_mth_txt VARCHAR2(2);
  lv_yr_txt VARCHAR2(4);
 BEGIN
  SELECT ADD_MONTHS(pledgedate,1)
    INTO lv_pl_dat
    FROM dd_pledge
    WHERE idpledge = p_id;
  lv_mth_txt := TO_CHAR(lv_pl_dat,'mm');
  lv_yr_txt := TO_CHAR(lv_pl_dat,'yyyy');
  RETURN TO_DATE((lv_mth_txt || '-01-' || lv_yr_txt),'mm-dd-yyyy');
 END dd_paydate1_pf;

-- Determines LAST Payment Due Date based on ID.
 FUNCTION dd_payend_pf
  (p_id IN dd_pledge.idpledge%TYPE)
  RETURN DATE
  IS
  lv_pay1_dat DATE;
  lv_mths_num dd_pledge.paymonths%TYPE;
 BEGIN
  SELECT dd_paydate1_pf(idpledge), paymonths - 1                        -- LINE 28
    INTO lv_pay1_dat, lv_mths_num
    FROM dd_pledge
    WHERE idpledge = p_id;
  IF lv_mths_num = 0 THEN
     RETURN lv_pay1_dat;
  ELSE
     RETURN ADD_MONTHS(lv_pay1_dat, lv_mths_num);
  END IF;
 END dd_payend_pf;

-- Displays Donor Name, ID, First, Last payment using Donor ID
 PROCEDURE DD_PLIST_PP
    (p_id IN DD_PLEDGE.IDPLEDGE%TYPE,
     p_first DD_DONOR.FIRSTNAME%TYPE,
     p_last DD_DONOR.LASTNAME%TYPE,
     p_payfirst OUT DATE,
     p_paylast OUT DATE)
 AS
    lv_first DD_DONOR.FIRSTNAME%TYPE;
    lv_last DD_DONOR.LASTNAME%TYPE;
 BEGIN
    SELECT FIRSTNAME, LASTNAME
     INTO lv_first, lv_last
     FROM DD_DONOR
     WHERE p_id = IDDONOR;

    p_payfirst := PLEDGE_PKG.DD_PAYDATE1_PF(p_id);
    p_paylast := PLEDGE_PKG.DD_PAYEND_PF(p_id);
 END DD_PLIST_PP;

END;

Я получаю ошибку из второго блока.

LINE/COL ERROR
-------- -----------------------------------------------------------------
28/3     PL/SQL: SQL Statement ignored
28/10    PL/SQL: ORA-00904: : invalid identifier
28/10    PLS-00231: function 'DD_PAYDATE1_PF' may not be used in SQL

SO запрашивает больше текста и меньше кода. :) Моя проблема в том, что я хочу вызвать PLEDGE_PKG.DD_PLIST_PP и вернуть имя, а две даты назад. Надеюсь, это объяснение поможет, и спасибо за помощь.

Ответы [ 2 ]

2 голосов
/ 06 мая 2020

Как вы отметили, строка # 28 - это

SELECT dd_paydate1_pf(idpledge), paymonths - 1

Это выражение SQL, и вы используете его в пакете (это PL / SQL). SQL engine не может получить доступ к функции, которая является частной для этого пакета, если вы не сделаете его publi c. Следовательно, что будет работать, это

  • то, что вы использовали в строке # 55:

    p_payfirst := PLEDGE_PKG.DD_PAYDATE1_PF(p_id);
    

    , поскольку здесь все находится в PL / SQL (хотя это сомнительно, поскольку вы передаете значение параметра P_ID из другой таблицы в строке # 28) или

  • сделайте функцию publi c, объявив ее в спецификации пакета

[РЕДАКТИРОВАТЬ]

На основе вашего комментария (слишком много текста для ответа через другой комментарий):

Это примерно переключение контекста . select dd_paydate1_pf ... - SQL вызывается из PL / SQL. SQL пытается выбрать результат функции и не может найти эту функцию на уровне SQL. Если вы указали перед ним имя пакета, например select pledge_pkg.dd_paydate1_pf..., оно все равно не будет работать, поскольку эта функция не объявлена ​​в пакете спецификация , но является частной для пакета body .

Что вы можете сделать, так это переписать функцию как:

FUNCTION dd_payend_pf
  (p_id IN dd_pledge.idpledge%TYPE)
  RETURN DATE
  IS
  lv_pay1_dat DATE;
  lv_mths_num dd_pledge.paymonths%TYPE;
 BEGIN
  lv_pay1_dat := dd_paydate1_pf(p_id);  --> move it out of SELECT

  SELECT paymonths - 1
    INTO lv_mths_num
    FROM dd_pledge
    WHERE idpledge = p_id;

  IF lv_mths_num = 0 THEN
     RETURN lv_pay1_dat;
  ELSE
     RETURN ADD_MONTHS(lv_pay1_dat, lv_mths_num);
  END IF;
 END dd_payend_pf;

Посмотрим, поможет ли это.

0 голосов
/ 06 мая 2020

Вы не можете использовать частную функцию как часть запроса sql. Я добавил этот ответ поздно, так как мой первый ответ не касался проблемы, и теперь я вижу, что здесь есть хороший ответ. Но просто для информации, и я надеюсь, что это поможет в отношении использования DATE, вот забавный пакет, использующий частную функцию (которая просто возвращает день месяца, в который вам платят) и функцию publi c, которая возвращает вашу следующую оплату день:

create or replace package date_test is
    -- public:
    function pay_day return date;
end;
/

create or replace package body date_test is
    -- private function:
    function paid_on return number is
    begin
        return 24;
    end;

    -- public:
    function pay_day return date is
        -- use private function:
        v_paydate_DD number := paid_on;
        v_next_payday date;
    begin
        v_next_payday := trunc(sysdate,'MONTH') + v_paydate_DD - 1;
        if trunc(sysdate) >= trunc(v_next_payday)  then
          select add_months(v_next_payday,1) into v_next_payday from dual;
        end if;
        return v_next_payday;
    end;
end;
/


select date_test.pay_day from dual;
...