Error raised while creating the trigger in oracle apex - oracle

Compilation failed, line 3 (14:38:54) The line numbers associated with compilation errors are relative to the first BEGIN statement. This only affects the compilation of database triggers.
PLS-00382: expression is of wrong typeCompilation failed, line 3 (14:38:54) The line numbers associated with compilation errors are relative to the first BEGIN statement. This only affects the compilation of database triggers.
PL/SQL: Statement ignored
CREATE TABLE "P1_EXAM_TIMETABLE"
( "EXAM_ID" NUMBER(7,0),
"SCH_NO" NUMBER(4,0),
"CLASS_CODE" VARCHAR2(3) COLLATE "USING_NLS_COMP",
"EXAM_NAME" VARCHAR2(11) COLLATE "USING_NLS_COMP",
"MAX_MARKS" NUMBER(3,0),
"EXAM_DATE" DATE,
"SCH_SESSION" VARCHAR2(7) COLLATE "USING_NLS_COMP",
"SUBJECT" VARCHAR2(20) COLLATE "USING_NLS_COMP",
"CREATED_BY" VARCHAR2(30) COLLATE "USING_NLS_COMP",
"CREATED_ON" NUMBER,
"UPDATED_BY" VARCHAR2(30) COLLATE "USING_NLS_COMP",
"UPDATED_ON" NUMBER,
CONSTRAINT "P1_EXAM_TIMETABLE_PK" PRIMARY KEY ("EXAM_ID")
USING INDEX ENABLE
) DEFAULT COLLATION "USING_NLS_COMP"
/
CREATE OR REPLACE TRIGGER BI_P1_EXAM_TIMETABLE BEFORE
INSERT ON P1_EXAM_TIMETABLE
FOR EACH ROW
BEGIN
:new.EXAM_ID:= P1_EXAM_TIMETABLE_SEQ.nextval;
:new.CREATED_ON:= SYSDATE;
:new.CREATED_BY:= nvl(v('APP_USER'), 'SYSTEM');
:new.UPDATED_BY:= NULL;
:new.UPDATED_ON:= NULL;
END;
Does Any one know where am i missing, please help me to fix this

Of course, it won't work. This is what you're doing:
Table:
"CREATED_ON" NUMBER, --> number datatype
Trigger:
:new.CREATED_ON:= SYSDATE; --> SYSDATE is a function that returns DATE datatype
You can't put date into a numeric datatype. What to do? Fix column datatype and change it to DATE.
SQL> create table p1_exam_timetable(
2 exam_id number(7,0),
3 sch_no number(4,0),
4 class_code varchar2(3),
5 exam_name varchar2(11),
6 max_marks number(3,0),
7 exam_date date,
8 sch_session varchar2(7),
9 subject varchar2(20),
10 created_by varchar2(30),
11 created_on date, --> Here! Was NUMBER
12 updated_by varchar2(30),
13 updated_on number,
14 constraint p1_exam_timetable_pk primary key(exam_id)
15 using index enable
16 )
17 /
Table created.
SQL> create sequence p1_exam_timetable_seq;
Sequence created.
Trigger:
SQL> create or replace trigger bi_p1_exam_timetable before
2 insert on p1_exam_timetable
3 for each row
4 begin
5 :new.exam_id := p1_exam_timetable_seq.nextval;
6 :new.created_on := sysdate;
7 :new.created_by := nvl(v('APP_USER'), 'SYSTEM');
8 :new.updated_by := null;
9 :new.updated_on := null;
10 end;
11 /
Trigger created.
Testing:
SQL> insert into p1_exam_timetable (exam_id) values (1);
1 row created.
SQL> select * from p1_exam_timetable;
EXAM_ID SCH_NO CLA EXAM_NAME MAX_MARKS EXAM_DATE SCH_SES
---------- ---------- --- ----------- ---------- ---------- -------
SUBJECT CREATED_BY CREATED_ON
-------------------- ------------------------------ ----------
UPDATED_BY UPDATED_ON
------------------------------ ----------
1
SYSTEM 22.02.2021
SQL>
P.S. When working with Oracle, don't use double quotes, they cause nothing but problems.

Related

Retain Original Value/State of "GENERATED AS IDENTITY" Column

I have a table that has a unique increment column created using command "GENERATED AS IDENTITY" and the data type is NUMBER(20,0).
Now, the dev want to change the data type to NUMBER(19,0). Since there are hundreds of tables and millions of data, creating a new set of tables just for one column change is not ideal.
So, I manage to create a workflow as follows:
ALTER TABLE my_schema.my_table
ADD REC_ID_TEMP NUMBER(19,0);
UPDATE my_schema.my_table
SET REC_ID_TEMP = REC_ID;
ALTER TABLE my_schema.my_table
DROP COLUMN REC_ID;
ALTER TABLE my_schema.my_table
ADD REC_ID NUMBER(19,0) GENERATED BY DEFAULT AS IDENTITY;
UPDATE my_schema.my_table
SET REC_ID = REC_ID_TEMP;
ALTER TABLE my_schema.my_table
MODIFY REC_ID NUMBER(19,0) GENERATED AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1515 //Must start with the last REC_ID value +1// CACHE 20 NOORDER NOCYCLE NOKEEP NOSCALE NOT NULL ENABLE;
This is the best way possible that I can think of with this peanut size brain of mine.
This way, I can make sure that the REC_ID is the exact same as before I change the data type.
But, the problem is, if you read my the last line of my script, I have to check the last REC_ID for the hundreds of table before I can run the last script. I need help to figure out a way that I can retain the original state.
You can use the START WITH LIMIT VALUE clause instead of specifying a number. From the documentation:
START WITH LIMIT VALUE, which is specific to identity_options, can only be used with ALTER TABLE MODIFY. If you specify START WITH LIMIT VALUE, then Oracle Database locks the table and finds the maximum identity column value in the table (for increasing sequences) or the minimum identity column value (for decreasing sequences) and assigns the value as the sequence generator's high water mark. The next value returned by the sequence generator will be the high water mark + INCREMENT BY integer for increasing sequences, or the high water mark - INCREMENT BY integer for decreasing sequences.
A little automation can make this as easily as calling a procedure per table, eg
SQL>
SQL> create table t1 as select * from scott.emp;
Table created.
SQL> create table t2 as select * from dba_objects;
Table created.
SQL> create table t3 as select * from dba_procedures;
Table created.
SQL>
SQL> alter table t1 add rec_id number(20) generated by default as identity start with 100000;
Table altered.
SQL> alter table t2 add rec_id number(20) generated by default as identity start with 100000;
Table altered.
SQL> alter table t3 add rec_id number(20) generated by default as identity start with 100000;
Table altered.
SQL>
SQL> update t1 set rec_id = rownum;
14 rows updated.
SQL> update t2 set rec_id = rownum;
81264 rows updated.
SQL> update t3 set rec_id = rownum;
37644 rows updated.
SQL>
SQL> alter table t1 add primary key ( rec_id);
Table altered.
SQL> alter table t2 add primary key ( rec_id);
Table altered.
SQL> alter table t3 add primary key ( rec_id);
Table altered.
SQL>
SQL> alter table t1 modify rec_id number(20) generated always as identity start with 100000;
Table altered.
SQL> alter table t2 modify rec_id number(20) generated always as identity start with 100000;
Table altered.
SQL> alter table t3 modify rec_id number(20) generated always as identity start with 100000;
Table altered.
SQL>
SQL> create or replace
2 procedure fix_up_my_recid(p_table varchar2) is
3 begin
4 execute immediate 'alter table '||p_table||' add tmp$rec_id number(19,0)';
5
6 execute immediate 'update '||p_table||' set tmp$rec_id = rec_id';
7
8 execute immediate 'alter table '||p_table||' set unused column rec_id';
9
10 execute immediate 'alter table '||p_table||' add rec_id number(19,0) generated by default as identity';
11
12 execute immediate 'update '||p_table||' set rec_id = tmp$rec_id';
13
14 execute immediate 'alter table '||p_table||' set unused column tmp$rec_id';
15
16 execute immediate 'alter table '||p_table||' move online';
17
18 execute immediate 'alter table '||p_table||' modify rec_id number(19,0) generated always as identity start with limit value';
19
20 end;
21 /
Procedure created.
SQL> sho err
No errors.
SQL>
SQL>
SQL>
SQL> desc t1
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
EMPNO NOT NULL NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)
REC_ID NOT NULL NUMBER(20)
SQL> exec fix_up_my_recid('T1')
PL/SQL procedure successfully completed.
SQL> exec fix_up_my_recid('T2')
PL/SQL procedure successfully completed.
SQL> exec fix_up_my_recid('T3')
PL/SQL procedure successfully completed.
SQL> desc t1
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
EMPNO NOT NULL NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)
REC_ID NOT NULL NUMBER(19)
SQL>
SQL>
SQL>
SQL>
SQL> desc t2
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
OWNER VARCHAR2(128)
OBJECT_NAME VARCHAR2(128)
SUBOBJECT_NAME VARCHAR2(128)
OBJECT_ID NUMBER
DATA_OBJECT_ID NUMBER
OBJECT_TYPE VARCHAR2(23)
CREATED DATE
LAST_DDL_TIME DATE
TIMESTAMP VARCHAR2(19)
STATUS VARCHAR2(7)
TEMPORARY VARCHAR2(1)
GENERATED VARCHAR2(1)
SECONDARY VARCHAR2(1)
NAMESPACE NUMBER
EDITION_NAME VARCHAR2(128)
SHARING VARCHAR2(18)
EDITIONABLE VARCHAR2(1)
ORACLE_MAINTAINED VARCHAR2(1)
APPLICATION VARCHAR2(1)
DEFAULT_COLLATION VARCHAR2(100)
DUPLICATED VARCHAR2(1)
SHARDED VARCHAR2(1)
CREATED_APPID NUMBER
CREATED_VSNID NUMBER
MODIFIED_APPID NUMBER
MODIFIED_VSNID NUMBER
REC_ID NOT NULL NUMBER(19)
SQL> desc t3
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
OWNER VARCHAR2(128)
OBJECT_NAME VARCHAR2(128)
PROCEDURE_NAME VARCHAR2(128)
OBJECT_ID NUMBER
SUBPROGRAM_ID NUMBER
OVERLOAD VARCHAR2(40)
OBJECT_TYPE VARCHAR2(13)
AGGREGATE VARCHAR2(3)
PIPELINED VARCHAR2(3)
IMPLTYPEOWNER VARCHAR2(128)
IMPLTYPENAME VARCHAR2(128)
PARALLEL VARCHAR2(3)
INTERFACE VARCHAR2(3)
DETERMINISTIC VARCHAR2(3)
AUTHID VARCHAR2(12)
RESULT_CACHE VARCHAR2(3)
ORIGIN_CON_ID NUMBER
POLYMORPHIC VARCHAR2(5)
REC_ID NOT NULL NUMBER(19)
SQL>
Note that I've not used DROP COLUMN because that's incredibly expensive. Generally better to just go with SET UNUSED, and I've thrown in an MOVE ONLINE at the end because all those updates could make a mess of your table row structure.

Error report - ORA-02330: datatype specification not allowed 02330. 00000 - when creating object relational table

I was trying to create two object-relational tables and wanted to create a foreign key in the department table to reference the primary key of the employee table.
It throws "ORA-02330: datatype specification not allowed" when I try to create the DEPARTMENT TABLE when I try to create the department table.
CREATE or Replace TYPE EMP_TYPE AS OBJECT
(
EMP_ID INTEGER,
EMP_FNAME VARCHAR2(20),
EMP_LNAME VARCHAR2(25),
ADDRESS VARCHAR(30),
SALARY NUMBER (10),
JOB_TITLE VARCHAR2 (20));
CREATE TABLE EMPLOYEE OF EMP_TYPE (EMP_ID PRIMARY KEY);
CREATE OR REPLACE TYPE DEPT_TYPE AS OBJECT(
DEPT_ID INTEGER,
EMP_ID INTEGER,
DEPT_NAME VARCHAR(20),
LOCATION VARCHAR(20));
CREATE TABLE DEPARTMENT OF DEPT_TYPE (EMP_ID FOREIGN KEY REFERENCES EMPLOYEE(EMP_ID))
The last line is the one that throws the error
Wrong syntax. Should be
SQL> CREATE OR REPLACE TYPE EMP_TYPE AS OBJECT
2 (
3 EMP_ID INTEGER,
4 EMP_FNAME VARCHAR2 (20),
5 EMP_LNAME VARCHAR2 (25),
6 ADDRESS VARCHAR (30),
7 SALARY NUMBER (10),
8 JOB_TITLE VARCHAR2 (20)
9 );
10 /
Type created.
SQL> CREATE TABLE EMPLOYEE OF EMP_TYPE
2 ( EMP_ID PRIMARY KEY );
Table created.
SQL> CREATE OR REPLACE TYPE DEPT_TYPE AS OBJECT
2 (
3 DEPT_ID INTEGER,
4 EMP_ID INTEGER,
5 DEPT_NAME VARCHAR (20),
6 LOCATION VARCHAR (20)
7 );
8 /
Type created.
SQL> CREATE TABLE DEPARTMENT OF DEPT_TYPE
2 ( EMP_ID REFERENCES EMPLOYEE (EMP_ID) );
Table created.
SQL>

How to automate sql file execution?

We have CICD in place from long time for our application. Now, we are planning to automate DB script execution as well.
PFB the requirement.
For example, we have create_tables.sql and alter_tables.sql files.
create_tables.sql contains:
CREATE TABLE EMPLOYEE_DETAILS (
EMP_ID VARCHAR2(128 CHAR) NOT NULL,
FIRSTNAME VARCHAR2(128 CHAR),
LASTNAME VARCHAR2(128 CHAR),
CONSTRAINT REQUESTSUBMITTERS_PK PRIMARY KEY ( EMP_ID )
);
alter_tables.sql contains:
--v0.1
ALTER TABLE EMPLOYEE_DETAILS MODIFY FIRSTNAME VARCHAR2(256);
COMMIT;
--v0.2
ALTER TABLE EMPLOYEE_DETAILS MODIFY FIRSTNAME VARCHAR2(256) NOT NULL;
ALTER TABLE EMPLOYEE_DETAILS MODIFY LASTNAME VARCHAR2(256) NOT NULL;
COMMIT;
--v0.3
ALTER TABLE EMPLOYEE_DETAILS ADD EMAIL VARCHAR2(256) NOT NULL;
COMMIT;
Assume that we have an environments ABC. This environment has all changes available in create_tables.sql file but only --v0.1 change from alter_tables.sql file.
Now, we just want to promote JUST --v0.2 change from alter_tables.sql file to this environment. Please note that we don't want to promote changes --v0.1 and --v0.3 from alter_tables.sql file.
So, in general there will be --v0.xxx (a unique incremental indicator) in each sql file to specify which sql statements to execute.
Can you please let me know what could be the way to automate with this approach.
Also, please feel free to suggest any other solution which you think is better than above one.
First of all, ALTER is a DDL and it implicitly commits before and after executing that command, so - explicitly COMMITting is pretty much useless.
From my point of view, your approach is too difficult to apply as you have all the changes stored in the same file so you'd have to parse it and extract only some - desired - parts of it to be executed.
How about creating a set of tables that contains what to apply? Here's an example:
SQL> create table t_change_master
2 (change number constraint pk_cha primary key,
3 change_date date,
4 cb_apply number(1) default 0 not null,
5 applied_date date
6 );
Table created.
SQL> create table t_change_detail
2 (id number constraint pk_chadet primary key,
3 change number constraint fk_chadet_cha references t_change_master (change),
4 command varchar2(200),
5 cb_ok number(1),
6 error varchar2(200)
7 );
Table created.
Some sample data:
SQL> insert into t_change_master (change, change_date, cb_apply)
2 select 1, date '2022-06-25', 1 from dual union all
3 select 2, date '2022-06-26', 0 from dual union all
4 select 3, date '2022-06-28', 0 from dual;
3 rows created.
Note that ID = 1 and 4 contain duplicate commands:
SQL> insert into t_change_detail (id, change, command)
2 select 1, 1, 'alter table test add firstname varchar2(20)' from dual union all
3 select 2, 1, 'alter table test modify firstname varchar2(15)' from dual union all
4 select 3, 2, 'alter table test add lastname varchar2(20)' from dual union all
5 select 4, 2, 'alter table test add firstname varchar2(30)' from dual union all
6 select 5, 3, 'alter table test add address varchar2(30)' from dual;
5 rows created.
SQL>
This is table that will be used in this playground:
SQL> create table test (id number);
Table created.
Procedure that does the job; in two nested loops, it reads rows from the master table which are scheduled to run (cb_apply = 1) but have not been ran yet (applied_date is null). Inner loop just reads what to do, executes it and logs whether it was successful or not.
Basically, you'd schedule its execution using DBMS_SCHEDULER (or DBMS_JOB, depending on your database version):
SQL> create or replace procedure p_change is
2 l_err varchar2(200);
3 begin
4 for cur_m in (select change
5 from t_change_master
6 where cb_apply = 1
7 and applied_date is null
8 )
9 loop
10 for cur_d in (select id, command
11 from t_change_detail
12 where change = cur_m.change
13 )
14 loop
15 begin
16 dbms_output.put_line(cur_d.command);
17 execute immediate cur_d.command;
18
19 update t_change_detail set
20 cb_ok = 1
21 where id = cur_d.id;
22 exception
23 when others then
24 l_err := sqlerrm;
25 update t_change_detail set
26 cb_ok = 0,
27 error = l_err
28 where id = cur_d.id;
29 end;
30 end loop;
31 update t_change_master set
32 applied_date = sysdate
33 where change = cur_m.change;
34 end loop;
35 end;
36 /
Procedure created.
SQL>
OK, let's try it. Setting date format, just to know what is what:
SQL> alter session set nls_date_format = 'dd.mm.yyyy hh24:mi:ss';
Session altered.
SQL> set serveroutput on;
SQL> begin
2 p_change;
3 end;
4 /
alter table test add firstname varchar2(20)
alter table test modify firstname varchar2(15)
PL/SQL procedure successfully completed.
SQL> select * from t_change_master;
CHANGE CHANGE_DATE CB_APPLY APPLIED_DATE
------- ------------------- ---------- -------------------
1 25.06.2022 00:00:00 1 05.07.2022 17:50:01
2 26.06.2022 00:00:00 0
3 28.06.2022 00:00:00 0
SQL> select * from t_change_detail;
ID CHANGE COMMAND CB_OK ERROR
--- ------- ---------------------------------------------- ------ ----------------------------------------
1 1 alter table test add firstname varchar2(20) 1
2 1 alter table test modify firstname varchar2(15) 1
3 2 alter table test add lastname varchar2(20)
4 2 alter table test add firstname varchar2(30)
5 3 alter table test add address varchar2(30)
SQL>
Let's now run change = 2:
SQL> update t_change_master set cb_apply = 1 where change = 2;
1 row updated.
SQL> begin
2 p_change;
3 end;
4 /
alter table test add lastname varchar2(20)
alter table test add firstname varchar2(30)
PL/SQL procedure successfully completed.
SQL> select * from t_change_master;
CHANGE CHANGE_DATE CB_APPLY APPLIED_DATE
------- ------------------- ---------- -------------------
1 25.06.2022 00:00:00 1 05.07.2022 17:50:01
2 26.06.2022 00:00:00 1 05.07.2022 17:50:58
3 28.06.2022 00:00:00 0
SQL> select * from t_change_detail;
ID CHANGE COMMAND CB_OK ERROR
--- ------- ---------------------------------------------- ------ ----------------------------------------
1 1 alter table test add firstname varchar2(20) 1
2 1 alter table test modify firstname varchar2(15) 1
3 2 alter table test add lastname varchar2(20) 1
4 2 alter table test add firstname varchar2(30) 0 ORA-01430: column being added already
exists in table
5 3 alter table test add address varchar2(30)
SQL>
Right; it kind of works.
Certainly, that piece of code (I wrote in a matter of minutes) could/should be improved, but that's just the general idea.
On the other hand, why wouldn't you do that using version control system, such as Git or Subversion? Good people developed these tools which are much, much more powerful that anything me (or you) could "invent" in such a short time. I guess it's worth to check these products.

PL/SQL: How to insert all records from table type to another table without for loop

I have probably trivial problem but I can't nail the logic quite right.
I have following types:
create or replace TYPE test_rec FORCE
AS OBJECT (ref_id NUMBER (20)
,ref_type VARCHAR2 (4));
create or replace TYPE test_ref_tbl FORCE
AS TABLE OF test_rec;
and actual table
CREATE TABLE my_tbl
( id number(10) NOT NULL,
ref_id varchar2(20) NOT NULL,
ref_type varchar2(4),
CONSTRAINT my_pk PRIMARY KEY (id)
);
Now, in one procedure I get variable test_ref_tbl with data and I have to insert everything to my_tbl, also id should be generated from sequence.
I managed to do this quite easily with for loop
FOR i IN 1 .. test_ref_tbl.COUNT LOOP
INSERT INTO my_tbl(id
,ref_id
,ref_type)
VALUES (my_test_sequence.NEXTVAL
,test_ref_tbl(i).ref_id
,test_ref_tbl(i).ref_type
);
END LOOP;
and everything works fine, but I got alot of flack for inserting data in for loop, I'm not plsql developer so maybe my colleagues are making my job harder just for the hell of it.
But to get back on topic, is there a way to do this without for loop?
Thanks
Yes, there is. Here's an example:
Creating test case first:
SQL> CREATE OR REPLACE TYPE test_rec FORCE AS OBJECT
2 (
3 ref_id NUMBER (20),
4 ref_type VARCHAR2 (4)
5 );
6 /
Type created.
SQL> CREATE OR REPLACE TYPE test_ref_tbl FORCE AS TABLE OF test_rec;
2 /
Type created.
SQL> CREATE TABLE my_tbl
2 (
3 id NUMBER (10) NOT NULL,
4 ref_id VARCHAR2 (20) NOT NULL,
5 ref_type VARCHAR2 (4),
6 CONSTRAINT my_pk PRIMARY KEY (id)
7 );
Table created.
SQL> CREATE SEQUENCE my_test_sequence;
Sequence created.
As data source, I'm using Scott's DEPT table.
SQL> DECLARE
2 l_tab test_ref_tbl;
3 BEGIN
4 SELECT test_rec (deptno, SUBSTR (dname, 1, 4))
5 BULK COLLECT INTO l_tab
6 FROM dept;
7
8 -- this is what you're looking for
9 INSERT INTO my_tbl (id, ref_id, ref_type)
10 SELECT my_test_sequence.NEXTVAL, t.*
11 FROM TABLE (l_tab) t;
12 END;
13 /
PL/SQL procedure successfully completed.
SQL> SELECT * FROM my_tbl;
ID REF_ID REF_
---------- -------------------- ----
1 10 ACCO
2 20 RESE
3 30 SALE
4 40 OPER
SQL>

Tips on how to calculate the age on PL/SQL

I created an SQL script:
{CREATE TABLE ator (
id NUMBER(3) NOT NULL,
nome_artistico VARCHAR2(25) NOT NULL,
nss NUMBER(11),
sexo CHAR(1),
nacionalidade VARCHAR2(15),
email VARCHAR2(30),
telefone_fixo VARCHAR2(12),
telefone_movel VARCHAR2(12),
dt_nascimento DATE,
CONSTRAINT ator_pk PRIMARY KEY ( id )
);}
My question is: Does anyone has a tip on how to calculate and print the age of x actor? I'm great at SQL but not so good on PL/SQL so if anyone could give a tip I would appreciate it a lot.
You can use months_between as follows:
Select t.*, trunc(months_between(sysdate, dt_nascimento) / 12) as age
From ator t
As you're interested in PL/SQL solution, I'd suggest you to use a function: pass actor's ID to it and return actor's age. A simple example:
SQL> create or replace function f_age (par_id in ator.id%type)
2 return number
3 is
4 l_age number;
5 begin
6 select round(months_between(sysdate, a.dt_nascimento) / 12)
7 into l_age
8 from ator a
9 where a.id = par_id;
10
11 return l_age;
12 end;
13 /
Function created.
SQL> select f_age(5) from dual;
F_AGE(5)
----------
54
SQL>

Resources