一尘不染

使用oracle plsql触发器从生日计算年龄并将年龄插入表中

sql

我有一张桌子

dates
(dob date,
age number(4)
);

我将插入出生日期,触发器将计算年龄并将该年龄插入“年龄”字段。

CREATE OR REPLACE PROCEDURE getage IS 
ndob date;
nage number(10);
BEGIN
select dob into ndob from dates; 
select (sysdate-to_date(ndob))/365 into nage from dual;
update dates set age=nage;
END;
/
SHOW ERRORS;

此过程可以正常工作,但仅适用于一行,并且我需要所有行都使用触发器,但是如果我从触发器中调用它,则会发生错误。

CREATE OR REPLACE TRIGGER agec after INSERT OR UPDATE ON dates
FOR EACH ROW
BEGIN
getage;
END; 
/

阅读 330

收藏
2021-03-17

共1个答案

一尘不染

请帮助…我真的需要这个…

不,你没有。我不确定您会注意什么;而且没有理由您应该:-)但是:

不要将年龄存储在数据库中。
绝对保证您偶尔会出错。每个人的年龄每年都在变化,但是对于某些人来说,它每天都在变化。反过来,这意味着您需要每天运行一个批处理作业并更新使用期限。如果此操作失败,或者不是
非常严格, 并且需要运行两次,那么您就遇到了麻烦。

您应该 始终 在需要时计算年龄。这是一个相当简单的查询,从长远来看可以为您省去很多麻烦。

select floor(months_between(sysdate,<dob>)/12) from dual

我设置了一些SQL Fiddle来演示


现在,要真正回答您的问题

此过程工作正常,但仅适用于一行,但是对于所有行,我都需要触发,但是如果我从触发器调用它,则会发生错误…

您没有提到错误,请稍后再试,因为它非常有帮助,但我怀疑您会收到

ORA-04091:表string.string正在变异,触发器/函数可能看不到它

这是因为您的过程正在查询正在更新的表。Oracle不允许这样做,以维护数据的读取一致视图。避免这种情况的方法是不查询表,而无需这样做。将您的过程更改为一个函数,该函数会在给定生日的情况下返回正确的结果:

function get_age (pDOB date) return number is
   /* Return the the number of full years between 
      the date given and sysdate.
      */    
begin    
   return floor(months_between(sysdate,pDOB)/12);    
end;

再次注意,months_between()由于并非所有年份都有365天,因此我正在使用该函数。

然后,在触发器中,将值直接分配给该列。

CREATE OR REPLACE TRIGGER agec before INSERT OR UPDATE ON dates
FOR EACH ROW
BEGIN
   :new.age := get_age(:new.dob);
END;

:new.<column>语法是用一个参考<column>正在更新。在这种情况下:new.age,将要在表中放入实际值。

这意味着您的表将自动更新,这是DML触发器的关键点

如您所见,该功能根本没有意义。你的触发器可以成为

CREATE OR REPLACE TRIGGER agec before INSERT OR UPDATE ON dates
FOR EACH ROW
BEGIN
   :new.age := floor(months_between(sysdate,:new,DOB)/12);
END;

但是,话虽如此,如果您打算在数据库中的其他位置使用此功能,请分开使用。好的做法是将在多个位置使用的代码保留在这样的函数中,以便始终以相同的方式使用。它还确保每当有人计算年龄时,他们都会正确地做到这一点。

除了一点,您确定要允许人们达到9,999岁吗?或0.000000000001998
(证明)数值精度基于
有效 位数。此(根据Oracle)仅是 非零
数字。您很容易被这个吸引住。数据库的目的是将可能的输入值限制为仅有效的那些值。我会认真考虑声明您的年龄列,number(3,0)以确保仅包括“可能”值。

2021-03-17