执行触发时出错。如何重新选择声明?

时间:2016-05-15 20:45:47

标签: oracle-sqldeveloper

我对触发器比较新,所以请原谅我,如果这看起来不应该如此。我正在创建一个触发器,用于检查用户帐户的上次付款日期,如果他们暂时没有付款,则将值设置为0。我创建了我认为正确的触发器,但是当它被触发时我得到错误,“执行触发器时出错”。据我所知,select语句在选择正在更改的值时会导致错误。这是我的代码。

CREATE OR REPLACE TRIGGER t
  BEFORE 
   UPDATE OF LASTLOGINDATE
   ON USERS
   FOR EACH ROW
DECLARE
   USER_CHECK NUMBER;
   PAYMENTDATE_CHECK DATE;
   ISACTIVE_CHECK CHAR(1);

BEGIN 
   SELECT U.USERID, U.ISACTIVE, UP.PAYMENTDATE 
   INTO USER_CHECK, PAYMENTDATE_CHECK, ISACTIVE_CHECK
   FROM USERS U JOIN USERPAYMENTS UP ON U.USERID = UP.USERID
   WHERE UP.PAYMENTDATE < TRUNC(SYSDATE-60);

  IF ISACTIVE_CHECK = 1 THEN
    UPDATE USERS U
    SET ISACTIVE = 0
    WHERE U.USERID = USER_CHECK;

    INSERT INTO DEACTIVATEDUSERS
    VALUES(USER_CHECK,SYSDATE);

 END IF;


 END;

从我的想法,因为select在begin语句中,它将在更新之前运行,没有任何关于表的更改,直到通过触发器之后。我试过但是在选择变量前面使用:old,但这似乎不是正确的用法。

这是我正在尝试的更新声明。

UPDATE USERS 
SET LASTLOGINDATE = SYSDATE
WHERE USERID = 5;

1 个答案:

答案 0 :(得分:0)

一些问题:

  1. 您在触发器中执行的select会将变量isactive_check设置为付款日期,反之亦然。那里有意外切换,这将对下一个if产生负面影响;

  2. 相同的select应该只返回一条记录,由于您加入了表格userpayments,因此无法保证,因为您可能会为所选用户多次付款满足条件,或根本没有。更改select以进行汇总。

  3. 如果用户有多个付款记录,则一个条件可能为true,但对另一个条件可能不是。因此,如果您只对长期未付款的用户感兴趣,则不应包括此类用户,即使他们有旧的付款记录。相反,您应该检查所有记录是否符合条件。这可以使用having子句。

  4. 由于表users正在变异(更新触发器在该表上),因此您无法对同一个表执行每个操作,否则会导致一种死锁。这意味着您需要重新考虑触发器的用途。由于这是关于特定用户的更新,您实际上不需要检查整个表,而只需要检查正在更改的记录。为此,您可以使用特殊的new变量。

  5. 我建议使用这个SQL:

    SELECT   MAX(UP.PAYMENTDATE)
    INTO     PAYMENTDATE_CHECK
    FROM     USERPAYMENTS
    WHERE    USERID = :NEW.USERID
    

    然后继续检查:

    IF :NEW.ISACTIVE = 1 AND PAYMENTDATE_CHECK < TRUNC(SYSDATE-60) THEN
        :NEW.ISACTIVE := 0;
    
        INSERT INTO DEACTIVATEDUSERS (USER_ID, DEACTIVATION_DATE)
        VALUES(USER_CHECK,SYSDATE);
    
    END IF;
    

    现在您已避免在表users中执行任何操作,并已通过:new“记录”进行检查和修改。

    此外,优良作法是在insert语句中提及列名,我在上面的代码中已经完成了(根据需要调整列名):

    确保编译触发器并且不会产生编译错误。