2012-09-08 82 views
2

我有一个表计算与Oracle PLSQL触发从生日的年龄和插入年龄表

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

根据需要继续计算它会更好吗?如果你直接插入蝙蝠的年龄,你将不得不一直更新它,否则你将会以错误的年龄结束。 – Fluffeh

回答

7

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

不,你不知道。我不确定你会注意,并没有理由你应该:-)但:

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

你应该总是计算你需要它的年龄。这是一个相当简单的查询,并且在较长时间内为您节省很多痛苦。

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),以确保只包含“可能的”值。

+0

感谢本真的帮助... :) – hitman047

3

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

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

看那SQL Fiddle

+0

谢谢你哦...现在我明白了,,,我不应该在数据库中存储年龄...... – hitman047

0

如果你想的年龄就可以从数据库中有两个选项:

  1. 定义包含年龄计算的看法,或
  2. 定义一个虚拟的列,它的功能相同

要去de精细这样一个观点:

CREATE OR REPLACE VIEW DATES_VIEW 
    AS SELECT DOB, 
      FLOOR(MONTHS_BETWEEN(SYSDATE, DOB)/12) AS AGE 
     FROM DATES 

这里的问题是,你必须要记住,你从DATES_VIEW选择,但需要更新日期是精神混乱。向表中添加虚拟列的IMO更清洁:

CREATE TABLE DATES 
    (DOB  DATE, 
    AGE  GENERATED ALWAYS AS (FLOOR(MONTHS_BETWEEN(SYSDATE, DOB)/12)) VIRTUAL); 

请注意,虚拟列无法更新。

两种方法都有助于确保AGE始终保持一致。

分享和享受。