执行触发器时出错。如何修改 select 语句?
Error during execution of trigger. How to rework select statement?
我对触发器比较陌生,如果这看起来不对劲,请原谅我。我正在创建一个触发器,用于检查用户帐户的上次付款日期,如果他们有一段时间没有付款,则将值设置为 0。我创建了我认为是正确的触发器,但在触发时出现错误 "error during execution of trigger"。据我了解,select 语句导致错误,因为它 selecting 正在更改的值。这是我的代码。
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 语句中,它会 运行 在更新之前,在 if 运行s 之前,表不会发生任何变化通过触发器。我试过但在 select 变量前使用 :old 但这似乎不是正确的用法。
这是我正在尝试的更新语句。
UPDATE USERS
SET LASTLOGINDATE = SYSDATE
WHERE USERID = 5;
一些问题:
您在触发器中执行的 select
将变量 isactive_check
设置为付款日期,反之亦然。那里有一个意外的开关,会对接下来有负面影响if
;
同样的select
应该return恰好一条记录,看样子不保证,因为你加入了tableuserpayments
,这可能会为满足条件的选定用户支付数笔款项,或者根本 none。更改 select
以进行聚合。
如果用户有多个支付记录,条件可能对一个为真,但对另一个不为真。因此,如果您只对长时间未付款的用户感兴趣,则不应包括此类用户,即使他们有旧的付款记录。相反,您应该检查 all 记录是否满足条件。您可以使用 having
子句来做到这一点。
由于 table users
正在发生变化(更新触发器在 table 上),您不能对同一个 [=60= 执行所有操作],否则会导致一种僵局。这意味着您需要重新考虑触发器的用途。由于这是针对特定用户的更新,您实际上不需要检查整个 table,而只需检查正在更改的记录。为此,您可以使用特殊的 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;
现在您已经避免在 table users
中执行任何操作,并通过 :new
"record".
进行了检查和修改
此外,在 insert
语句中提及列名是一种很好的做法,我在上面的代码中已经这样做了(根据需要调整列名):
确保触发器已编译且未产生编译错误。
我对触发器比较陌生,如果这看起来不对劲,请原谅我。我正在创建一个触发器,用于检查用户帐户的上次付款日期,如果他们有一段时间没有付款,则将值设置为 0。我创建了我认为是正确的触发器,但在触发时出现错误 "error during execution of trigger"。据我了解,select 语句导致错误,因为它 selecting 正在更改的值。这是我的代码。
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 语句中,它会 运行 在更新之前,在 if 运行s 之前,表不会发生任何变化通过触发器。我试过但在 select 变量前使用 :old 但这似乎不是正确的用法。
这是我正在尝试的更新语句。
UPDATE USERS
SET LASTLOGINDATE = SYSDATE
WHERE USERID = 5;
一些问题:
您在触发器中执行的
select
将变量isactive_check
设置为付款日期,反之亦然。那里有一个意外的开关,会对接下来有负面影响if
;同样的
select
应该return恰好一条记录,看样子不保证,因为你加入了tableuserpayments
,这可能会为满足条件的选定用户支付数笔款项,或者根本 none。更改select
以进行聚合。如果用户有多个支付记录,条件可能对一个为真,但对另一个不为真。因此,如果您只对长时间未付款的用户感兴趣,则不应包括此类用户,即使他们有旧的付款记录。相反,您应该检查 all 记录是否满足条件。您可以使用
having
子句来做到这一点。由于 table
users
正在发生变化(更新触发器在 table 上),您不能对同一个 [=60= 执行所有操作],否则会导致一种僵局。这意味着您需要重新考虑触发器的用途。由于这是针对特定用户的更新,您实际上不需要检查整个 table,而只需检查正在更改的记录。为此,您可以使用特殊的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;
现在您已经避免在 table users
中执行任何操作,并通过 :new
"record".
此外,在 insert
语句中提及列名是一种很好的做法,我在上面的代码中已经这样做了(根据需要调整列名):
确保触发器已编译且未产生编译错误。