我有一张桌子
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; /
请帮助…我真的需要这个…
不,你没有。我不确定您会注意什么;而且没有理由您应该:-)但是:
不要将年龄存储在数据库中。 绝对保证您偶尔会出错。每个人的年龄每年都在变化,但是对于某些人来说,它每天都在变化。反过来,这意味着您需要每天运行一个批处理作业并更新使用期限。如果此操作失败,或者不是 非常严格, 并且需要运行两次,那么您就遇到了麻烦。
您应该 始终 在需要时计算年龄。这是一个相当简单的查询,从长远来看可以为您省去很多麻烦。
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天,因此我正在使用该函数。
months_between()
然后,在触发器中,将值直接分配给该列。
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,将要在表中放入实际值。
: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)以确保仅包括“可能”值。
number(3,0)