我有一张桌子
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 天。
在触发器中,您可以将值直接分配给列。
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)
以确保仅包含“可能”的值。
定义一个包含年龄计算的视图,或者
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 始终保持一致。