如何在 oracle 中的包体中编写程序 - 错误?

how to write a procedure inside package body in oracle - error?

我有这个包,我在其中添加了一些值到 table,这有效....

create or replace PACKAGE BODY DC_LOAN_PKG
IS
PROCEDURE loan_book 
(
  loan_id IN DC_LOAN_EVIDENCE.ID%type,
  b_id    IN DC_LOAN_EVIDENCE.BOOK_ID%type,
  m_id    IN DC_LOAN_EVIDENCE.MEMBER_ID%type,
  d_ofl   IN DC_LOAN_EVIDENCE.DATE_OF_LOAN%type)
IS
BEGIN
  INSERT
  INTO DC_LOAN_EVIDENCE
  (
    ID,
    BOOK_ID,
    MEMBER_ID,
    DATE_OF_LOAN
  )
  VALUES
  (
    DC_SEQ.NEXTVAL,
    b_id,
    m_id,
    SYSDATE
  );
  COMMIT;
END loan_book;

现在,我想编写程序 return_book 和 extend_loan,我想在其中获取还书时的日期和延期后的新借阅日期。我做错了什么?这是示例....

PROCEDURE return_book
  (
    b_id  IN DC_LOAN_EVIDENCE.BOOK_ID%type,
    m_id  IN DC_LOAN_EVIDENCE.MEMBER_ID%type,
    r_dat IN DC_LOAN_EVIDENCE.RETURN_DATE%type
  )
IS
  dStartDate DATE := TO_DATE('2014-12-23', 'YYYY-MM-DD');
  dEndDate   DATE := TO_DATE('2015-01-23', 'YYYY-MM-DD');
BEGIN
  UPDATE DC_LOAN_EVIDENCE
  SET RETURN_DATE =r_dat
  WHERE MEMBER_ID =m_id
  AND BOOK_ID     =b_id
  AND RETURN_DATE BETWEEN dStartDate AND dEndDate ;
  COMMIT;
END return_book;
PROCEDURE extend_loan
  (
    b_id  IN DC_LOAN_EVIDENCE.BOOK_ID%type,
    m_id  IN DC_LOAN_EVIDENCE.MEMBER_ID%type,
    d_ofl   IN DC_LOAN_EVIDENCE.DATE_OF_LOAN%type
  )
IS
BEGIN
  UPDATE DC_LOAN_EVIDENCE
  SET DATE_OF_LOAN =d_ofl
  WHERE MEMBER_ID =m_id
  AND BOOK_ID     =b_id;
  COMMIT;
END extend_loan;
END DC_LOAN_PKG;

您需要将内部过程保留在主过程的声明块中。

类似于-

PROCEDURE return_book(
    b_id  IN DC_LOAN_EVIDENCE.BOOK_ID%type,
    m_id  IN DC_LOAN_EVIDENCE.MEMBER_ID%type,
    r_dat IN DC_LOAN_EVIDENCE.RETURN_DATE%type )
IS
  dStartDate DATE := TO_DATE('2014-12-23', 'YYYY-MM-DD');
  dEndDate   DATE := TO_DATE('2015-01-23', 'YYYY-MM-DD');
      PROCEDURE extend_loan(
          b_id  IN DC_LOAN_EVIDENCE.BOOK_ID%type,
          m_id  IN DC_LOAN_EVIDENCE.MEMBER_ID%type,
          d_ofl IN DC_LOAN_EVIDENCE.DATE_OF_LOAN%type )
      IS
      BEGIN
        UPDATE DC_LOAN_EVIDENCE
        SET DATE_OF_LOAN =d_ofl
        WHERE MEMBER_ID  =m_id
        AND BOOK_ID      =b_id;
        COMMIT;
      END extend_loan;
BEGIN
  UPDATE DC_LOAN_EVIDENCE
  SET RETURN_DATE =r_dat
  WHERE MEMBER_ID =m_id
  AND BOOK_ID     =b_id
  AND RETURN_DATE BETWEEN dStartDate AND dEndDate ;
  COMMIT;
END return_book;
END DC_LOAN_PKG;

我坚持要你把它包起来。通过这样的独立程序,您将引入依赖链。这可以避免使用包。你可以实现封装,它有很多优点,就像 Tom Kyte 说的那样 -

  • 打破依赖链(当你安装一个新的包主体时没有级联失效——如果 你有调用过程的过程——编译一个会使你的数据库无效)

  • 支持封装——我将被允许编写模块化的、易于理解的代码——而不是 然后 MONOLITHIC,不可理解的程序

  • 显着增加我的命名空间。包名称在模式中必须是唯一的,但我可以 许多程序跨同名程序包而不会发生冲突

  • 支持重载

  • 在需要时支持会话变量

  • 促进整体良好的编码技术,让您编写模块化代码的东西, 可以理解,合乎逻辑地组合在一起....

编辑如果你有独立的程序,那么你需要单独拥有它们。

CREATE OR replace PACKAGE BODY dc_loan_pkg 
IS 
  -- Procedure 1 
  PROCEDURE Loan_book(loan_id IN dc_loan_evidence.id%TYPE, 
                      b_id    IN dc_loan_evidence.book_id%TYPE, 
                      m_id    IN dc_loan_evidence.member_id%TYPE, 
                      d_ofl   IN dc_loan_evidence.date_of_loan%TYPE) 
  IS 
  BEGIN 
      INSERT INTO dc_loan_evidence 
                  (id, 
                   book_id, 
                   member_id, 
                   date_of_loan) 
      VALUES      ( dc_seq.NEXTVAL, 
                   b_id, 
                   m_id, 
                   SYSDATE ); 

      COMMIT; 
  END loan_book; 
  -- Procedure 2 
  PROCEDURE Return_book (b_id  IN dc_loan_evidence.book_id%TYPE, 
                         m_id  IN dc_loan_evidence.member_id%TYPE, 
                         r_dat IN dc_loan_evidence.return_date%TYPE) 
  IS 
    dstartdate DATE := To_date('2014-12-23', 'YYYY-MM-DD'); 
    denddate   DATE := To_date('2015-01-23', 'YYYY-MM-DD'); 
  BEGIN 
      UPDATE dc_loan_evidence 
      SET    return_date = r_dat 
      WHERE  member_id = m_id 
             AND book_id = b_id 
             AND return_date BETWEEN dstartdate AND denddate; 

      COMMIT; 
  END return_book; 
  -- Procedure 3 
  PROCEDURE Extend_loan(b_id  IN dc_loan_evidence.book_id%TYPE, 
                        m_id  IN dc_loan_evidence.member_id%TYPE, 
                        d_ofl IN dc_loan_evidence.date_of_loan%TYPE) 
  IS 
  BEGIN 
      UPDATE dc_loan_evidence 
      SET    date_of_loan = d_ofl 
      WHERE  member_id = m_id 
             AND book_id = b_id; 

      COMMIT; 
  END extend_loan; 
END dc_loan_pkg; 
/

您的原始过程 loan_book 有两个您不使用的参数 - 无论您作为 idbook_loan_date 传递的什么都会被忽略,并被序列值和当前日期分别。对 ID 使用序列是有意义的,但让用户传递自己的值会造成混淆,因此您或许应该删除该参数。您是使用 sysdate 还是贷款日期的传递值取决于您的业务规则,但目前充其量也令人困惑。一个妥协可能是将该参数默认为 sysdate,因此可以在需要时覆盖它。

您的 return_book 过程不会匹配 loan_book 创建的任何记录,因为它正在检查 return_date,它将为空(除非您已经在与此同时);如果您只能 return 一次贷款,那张支票似乎很奇怪。本质上,AND RETURN_DATE BETWEEN dStartDate AND dEndDate 不会匹配任何 return_date 为空的行。

你的 extend_loan 过程似乎确实有效,假设你正在传递正确的书籍和成员值。

SQL Fiddle.

如果您没有看到 date_of_loan 被扩展,那么您一定是向过程传递了错误的值。

但是,将图书和会员传递给您的新程序意味着一本书只能借给会员一次。如果您想允许同一本书被多次借阅,您应该传递 ID 值,因为它(大概!)是唯一的。也许您在 return_book 中的 return_date 过滤器实际上应该检查 date_of_loan 是最近的,以试图解决这种歧义。相反,传递 ID 会更好;明确寻找非 return 版书籍,and/or 特定 book/member 的最近贷款,在某种程度上也可以工作,但不太稳健。

在过程内部提交通常也被认为是不好的做法,最好让调用者决定是提交还是回滚。