我对触发器比较新,所以请原谅我,如果这看起来不应该如此。我正在创建一个触发器,用于检查用户帐户的上次付款日期,如果他们暂时没有付款,则将值设置为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;
答案 0 :(得分:0)
一些问题:
您在触发器中执行的select
会将变量isactive_check
设置为付款日期,反之亦然。那里有意外切换,这将对下一个if
产生负面影响;
相同的select
应该只返回一条记录,由于您加入了表格userpayments
,因此无法保证,因为您可能会为所选用户多次付款满足条件,或根本没有。更改select
以进行汇总。
如果用户有多个付款记录,则一个条件可能为true,但对另一个条件可能不是。因此,如果您只对长期未付款的用户感兴趣,则不应包括此类用户,即使他们有旧的付款记录。相反,您应该检查所有记录是否符合条件。这可以使用having
子句。
由于表users
正在变异(更新触发器在该表上),因此您无法对同一个表执行每个操作,否则会导致一种死锁。这意味着您需要重新考虑触发器的用途。由于这是关于特定用户的更新,您实际上不需要检查整个表,而只需要检查正在更改的记录。为此,您可以使用特殊的new
变量。
我建议使用这个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
语句中提及列名,我在上面的代码中已经完成了(根据需要调整列名):
确保编译触发器并且不会产生编译错误。