我有一张桌子
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;
/
答案 0 :(得分:8)
请帮助......我真的需要这个...
不,你没有。我不确定你会注意;并且没有理由你应该:-)但是:
不要在您的数据库中存储年龄。您绝对可以保证偶尔出错。每个人每年的年龄都会发生变化,但是,对于某些人来说,每天都会发生变化。这反过来意味着您需要每天运行批处理作业并更新年龄。如果此操作失败,或者非常严格并且运行两次,那么您就遇到了麻烦。
您应该始终在需要时计算年龄。这是一个相当简单的查询,从长远来看可以为您节省很多痛苦。
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
是将放在表中的实际值。
这意味着您的表格会自动更新,即the point of a DML trigger。
正如你所看到的那样,功能根本就没什么意义;你的触发器可以变成
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 (proof)? Numeric precision基于重要位数;这个(根据Oracle)只是非零数字。你很容易被这个抓住。数据库的要点是将可能的输入值限制为仅有效的输入值。我会认真考虑将您的年龄列声明为number(3,0)
,以确保只包含“可能”的值。
答案 1 :(得分:4)
如果您通过触发器执行此操作,则在一天之后该年龄可能会出错。这就是为什么要保存 数据库表中的年龄是不好的做法,没有人这样做。你需要的是一个观点。
create view person_age as
select person_id,
floor(months_between(trunc(sysdate),birthdate)/12) as age
from birthdays;
答案 2 :(得分:1)
如果您希望AGE可以从数据库中获得,则有两种选择:
定义这样的视图:
CREATE OR REPLACE VIEW DATES_VIEW
AS SELECT DOB,
FLOOR(MONTHS_BETWEEN(SYSDATE, DOB) / 12) AS AGE
FROM DATES
这个问题是你必须记住你从DATES_VIEW中选择,但需要更新DATES,这在精神上很混乱。 IMO向表中添加虚拟列更清晰:
CREATE TABLE DATES
(DOB DATE,
AGE GENERATED ALWAYS AS (FLOOR(MONTHS_BETWEEN(SYSDATE, DOB) / 12)) VIRTUAL);
请注意,无法更新虚拟列。
这两种方法都有助于确保AGE始终保持一致。
分享并享受。