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

xlpyo6sf  于 2023-10-16  发布在  Oracle
关注(0)|答案(3)|浏览(162)

我有一张table

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; 
/
bakd9h0s

bakd9h0s1#

请帮帮我,我真的需要这个。
不你不知道我不确定你会注意听没有理由你应该:-)但是:

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

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

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

我设置了一个小SQL Fiddle来演示
现在,为了回答你的问题
这个过程工作正常,但只有一行,但对于所有的行,我需要触发器,但如果我从一个触发器调用它,然后发生错误.
你没有提到错误,请在未来这样做,因为它是非常有帮助的,但我怀疑你得到
ORA-04091:表字符串。字符串正在发生变化,触发器/函数可能看不到它
这是因为您的过程正在查询正在更新的表。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.00000000001998(证明)?数值精度基于 * 有效 * 位数;这(根据Oracle)是 * 非零 * 数字。你很容易被这个抓住。数据库的意义在于将可能的输入值限制为只有那些有效的值。我会认真考虑将您的age列声明为number(3,0),以确保只包含“可能”的值。

e4yzc0pl

e4yzc0pl2#

如果你通过一个触发器来做,那么一天后年龄可能就错了。这就是为什么在数据库表中保存年龄是一个不好的做法,没有人这样做。你需要的是风景。

create view person_age as
  select person_id, 
    floor(months_between(trunc(sysdate),birthdate)/12) as age
    from birthdays;

查看SQL Fiddle

zqdjd7g9

zqdjd7g93#

如果您希望AGE从数据库中可用,则有两个选项:
1.定义一个包含年龄计算的视图,或者
1.定义一个执行相同操作的虚拟列
要定义此类视图,请执行以下操作:

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始终保持一致。

相关问题