0

I new at using PL/SQL and I want the following: I have this table on Oracle SQLcl

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)

);

And I want a PL/SQL (preferred anonymous) that update field of "YearsOfAge" by minusing 2020 from the "YearOfBirth" field. I could do that but my problem is that the table won't be updated until I insert the PL/SQL block again. So whenever I insert a new row, I have to insert my PL/SQL block again. I want to get the table updated whenever I insert/update a row, without a need to insert this block following a new row.

To be clearer, I just want to insert SL/SQL block one time after creating the table, then get the table's "YearsOfAge" updated whenever I insert/update/delete a row. So when I write "select * from Child;" I need to see the "YearsOfAge" with the new value that computed from subtracting 2020 from "YearOf Birth". My current PL/SQL is below:

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;
/
William Robertson
  • 12,552
  • 3
  • 33
  • 36
Aki
  • 33
  • 5
  • Why store the age when it is so trivial to calculate it whenever you select data? – Honeyboy Wilson Jul 01 '20 at 03:42
  • Have you considered using a [trigger](https://docs.oracle.com/en/database/oracle/oracle-database/19/lnpls/plsql-triggers.html#GUID-217E8B13-29EF-45F3-8D0F-2384F9F1D231)? – Abra Jul 01 '20 at 04:08
  • I did consider that but my trigger got muting – Aki Jul 01 '20 at 04:16
  • Could you show the right Trigger how would look like? – Aki Jul 01 '20 at 04:24
  • 1
    First off, your table design is seriously flawed. 1) your 'varchar' columns should be varchar2(n), where 'n' is the maximum length of the field. 2) YEAR_OF_BIRTH should be a DATE, not a NUMBER. Yes, DATE also includes month, day, hour, minute and second. So what? Call it DATE_OF_BIRTH, store the actual date of birth. 3) as others have said, its counterproductive to store 'age', seeing as it is a) constantly changing with the passage of time, and b) is trivial to calculate at run time. – EdStevens Jul 01 '20 at 13:03
  • Also, your mixed-case column names aren't what you think they are. Oracle handles case sensitivity differently that MSSQL. In the oracle world, instead of MixedCaseNames, we use UNDERSCORE_SEPARATED_NAMES. – EdStevens Jul 01 '20 at 13:04

1 Answers1

4

If you really need to store the age this way, some options are virtual columns, views, and triggers.

Virtual Column

With a virtual column, Oracle will automatically perform the calculation on the fly.

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

View

One downside of virtual columns is that they cannot use functions like SYSDATE, so the year has to be hard-coded. With a view, the expression can reference SYSDATE and will always be up-to-date:

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

Trigger (Warning)

You can also use a trigger to create the value when a row is inserted or updated:

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

But in practice, triggers are a pain to maintain. Which leads to the question: why do you want to store this information in the first place?

Good database design should minimize the amount of redundant data. There are always exceptions, but you should have a good reason for those exceptions, like an especially complicated calculation that you don't want others to get wrong, you can't create a PL/SQL function because of an unusual security constraint, etc. Calculating something as trivial as the age may cause more problems than it solves.

Jon Heller
  • 31,663
  • 6
  • 63
  • 114