1

任何人都可以帮助我使用下面的代码。有 3 个表:Customer_A1、Reservation_A1 和 Invoice_A1。我正在编写一个触发器,该触发器将在每次进行新预订时执行。

触发器会预加载invoice_id(inv_id)、reservation_id(res_id)、客户名字(cust_fname)、客户姓氏(cust_lname)和reservation_start_date的信息。

我的代码如下。创建触发器时没有编译错误。但是,当我在 Reservation 表中插入一个新行来执行触发器时,它会通知我我的触发器有一个错误

ORA-01422: 提取返回的行数超过了请求的行数。

CREATE OR REPLACE TRIGGER invoice_after_reservation_made
AFTER INSERT
ON RESERVATION_A1
FOR EACH ROW

DECLARE
inv_id INVOICE_A1.INV_ID%type;
res_id INVOICE_A1.res_id%type;
room_id INVOICE_A1.room_id%type;
cust_fname INVOICE_A1.cust_fname%type;
cust_lname INVOICE_A1.cust_lname%type;
reservation_start_date INVOICE_A1.reservation_start_date%type;
cust_id RESERVATION_A1.cust_id%type;

BEGIN
--read reservation_id
res_id:= :new.res_id;
--read room_id
room_id:= :new.room_id;
--read reservation_start_date
reservation_start_date:= :new.reservation_start_date;
--read customer_id
cust_id:= :new.cust_id;
--create new invoice_id
SELECT MAX(INVOICE_A1.inv_id)+1 INTO inv_id FROM INVOICE_A1;

-- import value from CUSTOMER_A1 table to variable cust_fname, cust_lname
Select CUSTOMER_A1.cust_fname,CUSTOMER_A1.cust_lname INTO 
cust_fname,cust_lname 
FROM CUSTOMER_A1 
WHERE CUSTOMER_A1.cust_id=cust_id;

-- Insert record into invoice table
INSERT INTO INVOICE_A1
VALUES (inv_id,res_id,room_id,cust_fname,cust_lname,null,TO_DATE(TO_CHAR(reservation_start_date),'DD/MM/YYYY'),null);


END;

注意:我已经在互联网上寻找解决方案,但没有雪茄。人们说问题主要来自返回多行的 Select 语句。但是,我在上面代码中的 Select 查询只返回一行。我还检查了表的数据,在 3 个表 Customer_A1、Reservation_A1 和 Invoice_A1 中没有违反实体和参照完整性。我什至将代码复制到单独的测试程序中,以在读取输入后打印出所有变量。测试程序运行良好。我现在投降。请帮我解决这个问题。我是新的 。谢谢

4

1 回答 1

2

问题出在声明中

Select CUSTOMER_A1.cust_fname,CUSTOMER_A1.cust_lname INTO 
cust_fname,cust_lname 
FROM CUSTOMER_A1 
WHERE CUSTOMER_A1.cust_id=cust_id;

您可能的意思是“从 CUSTOMER_A1 中查找数据,其中 CUSTOMER_A1.cust_id = 变量 'cust_id' 的值”。不幸的是,这不是它的解释方式。数据库将其读取为“从 CUSTOMER_A1 中查找数据,其中 CUSTOMER_A1.cust_id = CUSTOMER_A1.cust_id” - 换句话说,它正在将CUST_ID每一行的字段与其自身进行比较,发现它们相等(NULL 值除外) ,并从该行返回数据。

编写 PL/SQL 时要记住的一条好规则是“永远不要给变量与要操作的列同名”。考虑到这一点,您可能会考虑将触发器重写为:

CREATE OR REPLACE TRIGGER invoice_after_reservation_made
  AFTER INSERT
  ON RESERVATION_A1
  FOR EACH ROW
DECLARE
  vInv_id INVOICE_A1.INV_ID%type;
  vRes_id INVOICE_A1.res_id%type;
  vRoom_id INVOICE_A1.room_id%type;
  vCust_fname INVOICE_A1.cust_fname%type;
  vCust_lname INVOICE_A1.cust_lname%type;
  vReservation_start_date INVOICE_A1.reservation_start_date%type;
  vCust_id RESERVATION_A1.cust_id%type;
BEGIN
  --read reservation_id
  vRes_id:= :new.res_id;

  --read room_id
  vRoom_id:= :new.room_id;

  --read reservation_start_date
  vReservation_start_date:= :new.reservation_start_date;

  --read customer_id
  vCust_id:= :new.cust_id;

  --create new invoice_id
  SELECT MAX(INVOICE_A1.inv_id)+1 INTO vInv_id FROM INVOICE_A1;

  -- import value from CUSTOMER_A1 table to variable cust_fname, cust_lname
  Select CUSTOMER_A1.cust_fname,CUSTOMER_A1.cust_lname
    INTO vCust_fname, vCust_lname 
    FROM CUSTOMER_A1 
    WHERE CUSTOMER_A1.cust_id=cust_id;

  -- Insert record into invoice table
  INSERT INTO INVOICE_A1
  VALUES (vInv_id, vRes_id, vRoom_id, vCust_fname, vCust_lname, null, 
          TO_DATE(TO_CHAR(reservation_start_date),'DD/MM/YYYY'), null);
END invoice_after_reservation_made;
于 2018-08-19T11:43:06.723 回答