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

问题描述 投票:0回答:3

我有一张桌子

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; 
/
sql database oracle triggers plsql
3个回答
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
是将要放入表中的实际值。

这意味着您的表将自动更新,这就是 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; 

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

顺便说一句,你确定要让人活到9999岁吗?或者 0.000000000001998 (证明)数字精度基于有效位数;这(根据 Oracle)只是“非零”数字。你很容易被这个问题所困扰。数据库的要点是将可能的输入值限制为有效的值。我会认真考虑将您的年龄列声明为 number(3,0) 以确保仅包含“可能”的值。

    


4
投票

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

查看 
SQL 小提琴


1
投票

定义一个包含年龄计算的视图,或者
  1. 定义一个具有相同功能的虚拟列
  2. 定义这样一个视图:

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

© www.soinside.com 2019 - 2024. All rights reserved.