什么是更新行而不需要重新插入的正确 PL/SQL?

What is the right PL/SQL for updating rows without a need to reinsert it?

我刚开始使用 PL/SQL,我想要以下内容: 我在 Oracle SQLcl

上有这个 table
create table Child (
id varchar not null,
name varchar not null,
gender varchar not null,
YearOfBirth number(4) not null,
YearsOfAge number(4) null,

CONSTRAINT Pk primary key (id)

);

我想要一个 PL/SQL(首选匿名),通过从“YearOfBirth”字段中减去 2020 来更新“YearsOfAge”字段。我可以这样做,但我的问题是 table 在我再次插入 PL/SQL 块之前不会更新。因此,每当我插入新行时,我都必须再次插入 PL/SQL 块。我想在 insert/update 行时更新 table,而不需要在新行之后插入此块。

更清楚一点,我只想在创建 table 后插入一次 SL/SQL 块,然后每当我 [=23] 时更新 table 的“YearsOfAge” =]/删除一行。所以当我写“select * from Child;”我需要查看“YearsOfAge”以及从“YearOf Birth”中减去 2020 计算得出的新值。 我目前的PL/SQL如下:

begin
IF INSERTING THEN
update Child set YearsOfAge = 2020 - YearOfBirth;
ELSIF DELETEING THEN
update Child set YearsOfAge = 2020 - YearOfBirth;
ELSE
update Child set YearsOfAge = 2020 - YearOfBirth;
END IF;
END;
/

如果您确实需要以这种方式存储年龄,一些选项是虚拟列、视图和触发器。

虚拟专栏

有了虚拟列,Oracle 将自动即时执行计算。

SQL> create table Child
  2  (
  3      id          number not null,
  4      name        varchar2(10) not null,
  5      gender      varchar2(10) not null,
  6      YearOfBirth number(4) not null,
  7      YearsOfAge  number generated always as (2020 - yearOfBirth) null,
  8      constraint pk_child primary key (id)
  9  );

Table created.

SQL> insert into child(id, name, gender, yearOfBirth) values(1, 'A', 'female'    , 1990);

1 row created.

SQL> insert into child(id, name, gender, yearOfBirth) values(2, 'B', 'male'      , 2000);

1 row created.

SQL> insert into child(id, name, gender, yearOfBirth) values(3, 'C', 'non-binary', 2010);

1 row created.

SQL> select * from child;

        ID NAME       GENDER     YEAROFBIRTH YEARSOFAGE
---------- ---------- ---------- ----------- ----------
         1 A          female            1990         30
         2 B          male              2000         20
         3 C          non-binary        2010         10

查看

虚拟列的一个缺点是它们不能使用像 SYSDATE 这样的函数,因此必须对年份进行硬编码。有了视图,表达式可以引用 SYSDATE 并且永远是最新的:

create or replace view child_view as
select id, name, gender, yearOfBirth, extract(year from sysdate) - yearOfBirth yearsOfAge
from child;

触发(警告)

您还可以使用触发器在插入或更新行时创建值:

create or replace trigger child_trg
before update or insert on child
for each row
begin
    if updating('YEAROFBIRTH') or inserting then
        :new.yearsOfAge := extract(year from sysdate) - :new.yearOfBirth;
    end if;
end;
/   

但在实践中,触发器很难维护。这就引出了一个问题:你为什么要首先存储这些信息?

好的数据库设计应该尽量减少冗余数据量。例外总是有的,但是对于这些例外你应该有一个很好的理由,比如你不希望别人出错的特别复杂的计算,你不能创建一个 PL/SQL 函数因为一个不寻常的安全约束等等。计算像年龄这样琐碎的事情可能会导致比它解决的问题更多的问题。