繁体   English   中英

Oracle:需要触发器方面的帮助

[英]Oracle: Need help on triggers

大家好,又是我,我刚刚开始上 Oracle/数据库学位课程,几天前我发布了这个,得到了及时的答复,非常感谢。

学习Oracle,根据日期设置布尔值

跟进此事,考虑我有这两张表

CREATE TABLE CUSTOMER (
    CustID NUMBER(5) NOT NULL,
    CONSTRAINT CustID_PK PRIMARY KEY (CustID),
    
    PlanTypeNo Number(1) NOT NULL,
    CONSTRAINT PlanTypeNo_CustomerFK FOREIGN KEY (PlanTypeNo) REFERENCES PLANTYPE(PlanTypeNo),
    
    CustName VARCHAR2(50) NOT NULL,
    CustICNo VARCHAR2(9) NOT NULL,
    CustTelNo VARCHAR2(8) NOT NULL,
    CONSTRAINT UniqueCustomer UNIQUE (CustICNo,CustTelNo),
    CONSTRAINT Exact_IC_Length CHECK (LENGTH(CustICNo) = 9),
    CONSTRAINT Exact_Tel_Length CHECK (LENGTH(CustTelNo) = 8),
    
    PaymentModePref VARCHAR2(15) NOT NULL,
    CONSTRAINT PaymentModePref_Accepted CHECK (PaymentModePref IN ('Paypal','Cash','EFT')),
    
    LastBillPaidDate DATE,
    hasUnpaidBill NUMBER(1) NOT NULL,
    CONSTRAINT UnpaidBill_Boolean CHECK (hasUnpaidBill IN ('0', '1')),
    
    CompanyName VARCHAR2(50),
    CompanyRegNo VARCHAR2(10),
    CONSTRAINT If_NonResident CHECK ((((PlanTypeNo) = 1) OR (CompanyName IS NOT NULL AND CompanyRegNo IS NOT NULL)))
    
    
);

CREATE TABLE LOCATION (
    LocationID NUMBER(6) NOT NULL,
    CONSTRAINT LocationID_PK PRIMARY KEY (LocationID),
    
    CustID Number(5) NOT NULL,
    CONSTRAINT CustID_LocationFK FOREIGN KEY (CustID) REFERENCES CUSTOMER(CustID),
        
    LocationName VARCHAR2(40) NOT NULL,
    LocationAddress VARCHAR2(50) NOT NULL,
    CONSTRAINT UniqueNameAddress_Location UNIQUE (LocationName, LocationAddress)
        
);

我在编码方面比较有经验,但不是数据库程序/脚本/查询。 您将如何为数据库创建一种方法(可能是约束?)来检查有未付账单的所有者是否想要添加新位置?

我尽可能比较 CustID 外键以返回到 CUSTOMER 表并检查它们是否有任何大于 60 天的 LastBillPaidDate,或者创建一个触发器,如果​​当前日期 - LastBillPaidDate > 60,则将 hasUnpaidBill 变为“1”,然后在尝试插入一行时检查 hasUnpaidBill 的值是否为 1

希望它不会太混乱,我明白了逻辑,但不知道如何为数据库实现它:|

再次感谢!

这是一个示例触发器。 编写触发器时,您可以查询其他表,但通常无法查询正在插入/更新的表。 因此可以查询 CUSTOMER,但您应该使用:NEW 对象来引用当前插入的 LOCATION 记录上的字段。

set define off
CREATE OR REPLACE TRIGGER LOCATION.LOC_CUSTOMER_UNPAID
BEFORE INSERT 
FOR EACH ROW
DECLARE
    has_unpaid varchar2(1);
    unpaid_bill EXCEPTION;
    PRAGMA EXCEPTION_INIT( unpaid_bill, -20001 );
BEGIN

    select 'Y' into has_unpaid
    from CUSTOMER c
    where c.CustID = :new.CustID
      and lastBillPaidDate < sysdate-60;
    
    if has_unpaid = 'Y' then
        raise_application_error( -20001, 'Cannot create new location: Customer has unpaid bill.' );
    end if;
    
EXCEPTION when NO_DATA_FOUND then
    null; -- ignore
END;
/

set define off不是绝对必要的,但一些 IDE 会感到困惑,并要求您将 :NEW 的值设置为绑定变量。)

我会说数据库可能不是这种逻辑的最佳位置。 您可以在触发器中执行此操作,但有时会有点尴尬 - 如果您无法捕获错误并显示友好消息,则从触发器引发错误消息回溯可能在客户端看起来很糟糕。

编辑:我在这里定义自定义错误消息 过程有点曲折。 unpaid_bill EXCEPTION表示我们正在创建一个新的错误类型, PRAGMA EXCEPTION_INIT(unpaid_bill, -20001)表示将我们的错误与代码 #-20001 相关联,而raise_application_error(-20001)表示引发/返回该错误代码。

为了提供更多背景信息,如果您想阻止用户做某事(例如,如果他们有未结帐单,则添加位置),您有几个选择:

  • 约束有利于验证当前插入的行。 这通常是简单的逻辑,例如“PHONE_NUM 中没有字母”或“AGE 大于零”。 如果违反约束逻辑,则会引发异常并阻止插入。
  • 也可以使用BEFORE INSERT 触发器- 如果它引发异常,它将阻止插入。 它们比约束更慢、更复杂,难以调试,但也很灵活——您可以查询其他表、调用函数等。优秀的编码人员很少使用它们。
  • 使用应用程序逻辑几乎总是您的首选。 这是最灵活的选项,通常速度更快,并且您可以向用户显示比 Oracle 错误堆栈跟踪更有帮助的警告。
  • 如果您的面向用户的应用程序又大又复杂,或者您的用户可以访问数据库,那么API也是一种很好的模式。 在执行插入的存储过程或包中编写所有验证代码,并使您的应用程序/用户调用 API 而不是直接插入到表中。 您可以使用授权/权限来强制执行此模式,但我不会在这里讨论。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM