我想创建 AFTER UPDATE 触发器来更新 DATE OF BIRTH where (AGE < 20)

Posted

技术标签:

【中文标题】我想创建 AFTER UPDATE 触发器来更新 DATE OF BIRTH where (AGE < 20)【英文标题】:I want to create AFTER UPDATE trigger to update DATE OF BIRTH where (AGE < 20) 【发布时间】:2021-09-03 16:41:15 【问题描述】:

我想更新年龄小于 20 的表中的数据。所以我想更新 dob 并将其设置为年龄大于 20 的日期(通过使用更新后触发器)。我知道我说的没有任何意义,但这是我的教授给的任务。

我正在使用 Oracle Live SQL。

create table SYCS_DBMS(
    SID NUMBER(10),
    SNAME VARCHAR(50),
    DOB DATE,
    PRESENT DATE
);

ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-YYYY';

INSERT INTO SYCS_DBMS VALUES(1, 'ANKIT', '4-OCTOBER-2002', sysdate);
INSERT INTO SYCS_DBMS VALUES(2, 'AAKASH', '30-OCTOBER-2002', sysdate);
INSERT INTO SYCS_DBMS VALUES(3, 'DHRUV', '5-APRIL-2002', sysdate);
INSERT INTO SYCS_DBMS VALUES(4, 'DEERAJ', '5-AUGUST-2002', sysdate);
INSERT INTO SYCS_DBMS VALUES(5, 'ADIL', '11-MARCH-2003', sysdate);
INSERT INTO SYCS_DBMS VALUES(6, 'VIRAJ', '7-JULY-2002', sysdate);

CREATE OR REPLACE TRIGGER trUPDT
AFTER UPDATE
ON SYCS_DBMS
FOR EACH ROW
DECLARE 
STU_AGE NUMBER;
BEGIN
--TO CHECK THE AGE BY DATE OF BIRTH
SELECT MONTHS_BETWEEN(TO_DATE(sysdate,'DD-MON-YYYY'), TO_DATE(:NEW.DOB,'DD-MON-YYYY'))/12
INTO STU_AGE FROM DUAL;
END;

【问题讨论】:

计算20年前的日期。然后将DOB设置为LEAST(:NEW.DOB, DATE_20_YR_AGO) "...update表中age小于20"和"...update dob 并将其设置为 age 大于 20" 的日期听起来很矛盾。请描述输入数据是什么,应该执行什么操作以及您期望的结果数据是什么。 您的日期格式已关闭:如果模板为 DD-MON-YYYY,则该值应包含 2 位数的日期和 3 字符的月份,例如 04-OCT-2002。另外,我会将其作为 BEFORE INSERT 触发器来执行(您的示例实际上并没有进行任何更新!):修改 DOB 值作为原始 SQL 插入的一部分,而不是执行多个额外步骤在已应用后更改原始值。结果应该更快/更具可扩展性。 sysdatedobdate 类型,所以 to_date 函数在这里是多余的。您不需要将日期转换为日期。 @astentx - 将 to_date 应用于 DATE 比只是多余的更糟糕。它强制一个隐含的 TO_CHAR,这又依赖于 NLS_DATE_FORMAT 的控制设置。如果星星没有完美排列,就会导致错误,而不仅仅是额外的、不需要的工作。 【参考方案1】:

让我们沿着“Revision Lane”走一走,因为这段代码可以使用一些。但首先是关于格式化的工作。你需要学习如何去做。事实上,我告诉我的学生,如果他们提交未格式化的代码,他们的作业就会失败。我什至懒得读它。 格式化您的代码。我应该能够只看它就知道流程。你如何格式化并不重要,只要它始终保持一致即可。诚然,10 行代码并不是什么大不了的事,但养成好习惯的时间是在养成坏习惯之前。现在看看你所拥有的: 小心设置并取决于此处设置 NLS_DATE_FORMAT 没事。但是当你需要调用另一个例程时会发生什么 该开发人员还设置了 NLS_DATE_FORMAT 但与您的不同。 一旦列/参数是 date 数据类型,就永远不要在其上使用 to_date 函数,它已经是一个日期。 Oracle 提供了大量的日期处理函数并使日期算术可用。在这种情况下,sysdate 和 DOB 列都已经是日期,所以不要对它们执行 to_date()。

Your expression 

    months_between(to_date(sysdate,'dd-mon-yyyy'),
                   to_date(:new.dob,'dd-mon-yyyy'))/12

Becomes just simply 

    months_between(sysdate, :new.dob)/12

更进一步。在 plsql 块中不需要Select ... into,除非您实际上是从表(或视图等)中检索。您可以直接分配给变量。 所以有了这个和上面的:

   select months_between(to_date(sysdate, :new.dob)/12
     into stu_age 
     from dual; 
     
becomes a simple assignment:
    
    stu_age := months_between(to_date(sysdate, :new.dob)/12; 

此时您的触发器已减少到:

create or replace trigger trupdt
    after update
       on sycs_dbms
      for each row
declare 
    stu_age number;
begin 
    --to check the age by date of birth
    stu_age := months_between(sysdate,:new.dob)/12; 
end trupdt; 

不幸的是,它完全没用。您将 2 个日期之间的月份计算为数值(带小数位),但随后触发器结束,只是将该计算扔掉,什么也不做。我不认为这是你所追求的。您最初表示要更新 dob。那么这里有两个问题。

    DOB 定义为date,因此您的计算需要得出一个日期。这不是。 您有一个更新后触发器,但有一个更新后触发器 无法更改列值,您需要一个更新前触发器。

现在您可以通过计算值和日期计算来获得 DOB 值,并使用适当的逻辑来确定您是否应该这样做。但是@Barmar 的建议非常好。我会的。谢谢巴尔默。使用带有 sysdate 的 add_months 函数代替months_between 函数来计算20 年前的日期,然后使用least 函数来选择适当的值。还绕过局部变量直接分配给:new_dob。对此的最终触发变得简单:

create or replace trigger sycs_dbms_dob_ge20_bur
   before update
       on sycs_dbms
      for each row
begin
    :new.dob := least(:new.dob, add_months(sysdate, -240)); -- (20yrs * 12mon/yr)    
end sycs_dbms_dob_ge20_bur; 

我在这里整理了一个小提琴,它会遍历上面的每个步骤。对于明显的家庭作业,我通常不会提供完整的答案。但是,如果我分配了这个,我会在下一堂课上完成它。我希望你把这个带到课堂上,并与教授和其他学生讨论。不要只是以自己的身份提交,任何中途体面的教授都会很快赶上。但至少要仔细研究它的作用以及它是如何从你开始的地方到达那里的。

【讨论】:

感谢您的帮助,我真的很感激。我缺乏编程逻辑,所以我很难理解实际问题是什么以及如何解决它。我不会在作业中复制粘贴这段代码,但我会尝试理解逻辑并将其应用于解决其他问题。

以上是关于我想创建 AFTER UPDATE 触发器来更新 DATE OF BIRTH where (AGE < 20)的主要内容,如果未能解决你的问题,请参考以下文章

AFTER UPDATE 触发器上的变异表错误

对于50000行的表,MySQL触发器AFTER UPDATE执行速度极慢

AFTER UPDATE 触发器使用更新表中的聚合

条件AFTER UPDATE触发器

SQLite 创建 AFTER UPDATE 触发器

创建触发器以在更新表之后插入