Using Nested Table variables / Collections in SQL inside PL/SQL blocks - oracle

I first create an address_type object
CREATE TYPE address_type AS OBJECT
( line1 VARCHAR2(100)
, line2 VARCHAR2(100)
, line3 VARCHAR2(100)
, city VARCHAR2(50)
, state VARCHAR2(50)
, country VARCHAR2(50)
, zip VARCHAR2(10)
);
/
I create a nested table type of the above object.
CREATE TYPE address_table AS TABLE OF ADDRESS_TYPE;
/
I then create another object as follows:
CREATE TYPE telephone_number_type AS OBJECT
( country_code VARCHAR2(4)
, area_code VARCHAR2(10)
, phone_number VARCHAR2(10)
, extension VARCHAR2(10)
, number_type VARCHAR2(10)
);
/
And then I create a nested table type as follows:
CREATE TYPE telephone_number_table AS TABLE OF TELEPHONE_NUMBER_TYPE;
/
Now I create a table named person. Many of whose columns are not much useful in this question, except for the telephone_numbers column which is of nested table telephone_number_table type.
CREATE TABLE person
( personid INTEGER PRIMARY KEY
, fname VARCHAR2(50) NOT NULL
, mname VARCHAR2(50)
, lname VARCHAR2(50) NOT NULL
, email VARCHAR2(255) UNIQUE
, password VARCHAR2(255) NOT NULL
, birthdate DATE
, billing_address ADDRESS_TABLE
, delivery_address ADDRESS_TABLE
, telephone_numbers TELEPHONE_NUMBER_TABLE
, display_pic BLOB
, ts_registration TIMESTAMP
, ts_verification TIMESTAMP
, ts_last_updated TIMESTAMP
) NESTED TABLE billing_address STORE AS nt_billing_address
, NESTED TABLE delivery_address STORE AS nt_delivery_address
, NESTED TABLE telephone_numbers STORE AS nt_telephone_numbers
, LOB(display_pic) STORE AS SECUREFILE (
TABLESPACE users
ENABLE STORAGE IN ROW
CHUNK 4096
PCTVERSION 20
NOCACHE
NOLOGGING
COMPRESS HIGH
)
;
I then create a sequence for this:
CREATE SEQUENCE sq_personid;
To insert values into the person table I use an anonymous block as follows:
DECLARE
v_fname person.fname%TYPE := 'Yogeshwar';
v_mname person.mname%TYPE := '';
v_lname person.lname%TYPE := 'Rachcha';
v_email person.email%TYPE := 'yogeshrachcha#gmail.com';
v_password person.password%TYPE := 'mail_123';
v_birthdate person.birthdate%TYPE := TO_DATE('28-03-1987', 'DD-MM-YYYY');
v_telephone_numbers TELEPHONE_NUMBER_TABLE;
v_billing_address ADDRESS_TABLE;
v_delivery_address ADDRESS_TABLE;
BEGIN
v_telephone_numbers := TELEPHONE_NUMBER_TABLE
( TELEPHONE_NUMBER_TYPE('+91','22','123456','','Residence')
, TELEPHONE_NUMBER_TYPE('+91','22','456798','123','Office')
, TELEPHONE_NUMBER_TYPE('+91','','1234567890','','Mobile'));
v_billing_address := ADDRESS_TABLE (ADDRESS_TYPE ( 'Line 1', 'Line 2', 'Line 3', 'Mumbai', 'Maharashtra', 'India', '123456'));
v_delivery_address := ADDRESS_TABLE (ADDRESS_TYPE ( 'Line 1', 'Line 2', 'Line 3', 'Mumbai', 'Maharashtra', 'India', '123456'));
-- billing and delivery addresses are the same. These are not much importance in this question.
INSERT INTO person VALUES
( sq_personid.nextval
, v_fname
, v_mname
, v_lname
, v_email
, v_password
, v_birthdate
, v_billing_address
, v_delivery_address
, v_telephone_numbers
, NULL
, sysdate
, sysdate
, sysdate);
END;
Everything runs absolutely perfect till this point. Then in an anonymous block like the following, I try to create a nested table variable and use it in an SQL query:
DECLARE
TYPE t_country_codes IS TABLE OF VARCHAR2(4);
country_codes T_COUNTRY_CODES := T_COUNTRY_CODES('+1', '+44', '+91');
cc VARCHAR2(4);
BEGIN
FOR i IN (SELECT t.country_code
FROM person p
CROSS JOIN TABLE(p.telephone_numbers) t
WHERE t.country_code IN (SELECT COLUMN_VALUE -- I doubt the problem is with this SELECT statement.
FROM TABLE(country_codes))) LOOP
dbms_output.put_line(i.country_code);
END LOOP;
END;
/
I get this error:
ORA-06550: line 8, column 70:
PLS-00642: local collection types not allowed in SQL statements
ORA-06550: line 8, column 64:
PL/SQL: ORA-22905: cannot access rows from a non-nested table item

A nested table type can be declared either in SQL (via the CREATE TYPE statement like you did with the telephone_number_table type) or in PL/SQL (via the TYPE declaration on the DECLARE block). If you declare the type in PL/SQL, you cannot use an instance of the type in SQL. You would need to declare the type in SQL in order to use an instance of the type in SQL.

Please, try to move your "local" (from last block) collection definition to the schema level.
Also, this post can be helpful.

Related

Invalid reference in Oracle database

I am using Oracle database 19c for a project. I have created the alumni table as shown below:
CREATE TABLE Alumni (
ID NUMBER GENERATED ALWAYS AS IDENTITY ,
First_Name varchar2(20) NOT NULL,
Last_Name varchar2(20) NOT NULL,
Gender char(1) NOT NULL,
CHECK (Gender IN('M','F')),
Graduation_Year number NOT NULL,
check (Graduation_Year>=1980 AND Graduation_Year<=2020),
Degree_Course varchar2(255) not null,
Award_Nominated CHAR(1),
CHECK (Award_Nominated IN('Y','N')),
Award_Won CHAR(1),
CHECK (Award_Won IN('Y','N')),
Phone_Number VARCHAR2(15) not null,
Email_Address VARCHAR2(255) not null,
CONSTRAINT ALUM_PK PRIMARY KEY(ID)
);
Then I tried to define a function that will allow me to search by First or last name in a section of the table, shown below:
CREATE OR REPLACE TYPE alum_row_type is object(ID number,First_name varchar2(20),Last_Name
varchar2(20),Phone_number varchar2(15),Email_Address varchar2(255));
CREATE OR REPLACE TYPE t_alum_row_type as table of alum_row_type;
CREATE OR REPLACE FUNCTION Name_Search RETURN t_alum_row_type
IS
L_alum_row t_alum_row_type := t_alum_row_type();
X integer :=0;
BEGIN
FOR R IN (SELECT ID,First_Name, Last_Name,Phone_Number,Email_Address FROM alumni)
LOOP
L_alum_row.extend;
X:=X+1;
L_alum_row(X):= alum_row_type(X.ID,X.First_Name, X.Last_Name, X.Phone_Number, X.Email_Address);
END LOOP;
RETURN L_alum_row;
END;
After running this, I get this error:
LINE/COL ERROR
--------- -------------------------------------------------------------
10/9 PL/SQL: Statement ignored
10/41 PLS-00487: Invalid reference to variable 'X'
Errors: check compiler log
How can I resolve this issue. I am relatively new to PL\SQL, so please explain it simply.
This is not possible
L_alum_row(X):= alum_row_type(X.ID,X.First_Name, X.Last_Name, X.Phone_Number, X.Email_Address);
You are referencing X.ID, X.Phone_Number etc. . . but declared X as an integer. If you are trying to access the current loop record, try with
L_alum_row(X):= alum_row_type(R.ID,R.First_Name, R.Last_Name, R.Phone_Number, R.Email_Address);
As you declared the loop iterator as R for your cursor
FOR R IN (SELECT ID,First_Name, Last_Name,Phone_Number,Email_Address FROM alumni)
You can eliminate the loop altogether by using BULK COLLECT. Thereby reducing the function to basically a single select statement.
function name_search
return t_alum_row_type
is
l_alum_row t_alum_row_type := t_alum_row_type();
begin
select alum_row_type(id,first_name, last_name,phone_number,email_address)
bulk collect
into l_alum_row
from alumni;
return l_alum_row;
end;

Cast SYS_REFCURSOR to a PL/SQL Type

I have a stored procedure that calls a function that returns a SYS_REFCURSOR. The function returns a SYS_REFCURSOR because this function is also called from Java application and Java does not understand rowtype.
Here is my function.
function f_get_building(
p_building_id in T_BUILDING.ID%type
) return sys_refcursor
AS
v_cursor sys_refcursor;
BEGIN
open v_cursor for
select
BUILDING_ID,
CAMPUS_ID,
DELETE_FLAG,
max(EFFECTIVE_DATE),
END_DATE,
IMAGE_URL,
INSTITUTION_ID,
LOCAL_ID,
LOCATION_ID,
NAME
from V_BUILDING
where BUILDING_ID = p_building_id
group by
BUILDING_ID,
CAMPUS_ID,
DELETE_FLAG,
END_DATE,
IMAGE_URL,
INSTITUTION_ID,
LOCAL_ID,
LOCATION_ID,
NAME;
return v_cursor;
END f_get_building;
In another stored procedure I am also calling this function but having issues using it. Here is the stored procedure.
procedure sp_delete_building(
p_building_id in T_BUILDING.ID%type,
p_permanent_delete in boolean default false
)
AS
v_building_cur sys_refcursor;
v_building_rec V_BUILDING%rowtype;
BEGIN
-- if permanment delete
if p_permanent_delete = true
then
delete from T_BUILDING where ID = p_building_id;
-- otherwise perform soft delete
else
-- lookup
v_building_cur := f_get_building(p_building_id);
-- if cursor is empty there is nothing to do
if v_building_cur%notfound then
return;
end if;
fetch v_building_cur into v_building_rec; -- this line is where the error happens
-- if its already deleted nothing to do
if v_building_rec.DELETE_FLAG = 'Y'
then
return;
else
insert into T_BUILDING_ATTRIBUTE(BUILDING_ID,EFFECTIVE_DATE,DELETE_FLAG,
IMAGE_URL,LOCATION_ID,NAME)
values (v_building_rec.BUILDING_ID,current_timestamp,'Y',v_building_rec.IMAGE_URL
,v_building_rec.LOCATION_ID,v_building_rec."NAME");
end if;
end if;
END sp_delete_building;
I am getting the following PL/SQL stacktrace.
ORA-01722: invalid number
ORA-06512: at "OBR.PKG_BUILDING", line 114
ORA-06512: at line 8
Forgive my ignorance, this is my first project using PL/SQL, I would classify myself as a Java developer, not a database developer. Since I am selecting everything from V_BUILDING I expected I would just be able to case it as a rowtype inside the stored procedure. How can I use my function inside my stored procedure?
Update:
Here is the create statement for V_BUILDING
CREATE OR REPLACE FORCE VIEW "OBR"."V_BUILDING" ("BUILDING_ID", "LOCAL_ID", "INSTITUTION_ID", "EFFECTIVE_DATE", "END_DATE", "DELETE_FLAG", "CAMPUS_ID", "LOCATION_ID", "IMAGE_URL", "NAME") AS
SELECT
ba.BUILDING_ID,
b.LOCAL_ID,
b.INSTITUTION_ID,
ba.EFFECTIVE_DATE,
NVL(MIN(ba2.EFFECTIVE_DATE - INTERVAL '0.000001' SECOND),TO_DATE('31-DEC-9999', 'DD-MON-YYYY')) AS END_DATE,
ba.DELETE_FLAG,
ba.CAMPUS_ID,
ba.LOCATION_ID,
ba.IMAGE_URL,
ba.NAME
FROM
T_BUILDING b
INNER JOIN T_BUILDING_ATTRIBUTE ba
ON b.ID = ba.BUILDING_ID
LEFT JOIN T_BUILDING_ATTRIBUTE ba2
ON ba.BUILDING_ID = ba2.BUILDING_ID
AND ba2.EFFECTIVE_DATE > ba.EFFECTIVE_DATE
GROUP BY
ba.BUILDING_ID,
b.LOCAL_ID,
b.INSTITUTION_ID,
ba.EFFECTIVE_DATE,
ba.DELETE_FLAG,
ba.CAMPUS_ID,
ba.LOCATION_ID,
ba.IMAGE_URL,
ba.NAME
ORDER BY ba.BUILDING_ID, ba.EFFECTIVE_DATE DESC;
Update 2:
Here is a screenshot of the types in the view
CAMPUS_ID - NUMBER(10)
LOCATION_ID - NUMBER(10)
IMAGE_URL - VARCHAR(500)
NAME - VARCHAR(255)
BUILDING_ID - NUMBER(10)
LOCAL_ID - VARCHAR(30)
INSTITUTION_ID - NUMBER(10)
EFFECTIVE_DATE - TIMESTAMP(6)
END_DATE - TIMESTAMP (6)
DELETE_FLAG - CHAR(1)
Here are the list of columns and their datatypes as returned by the view and the ref cursor:
LIST OF COLS FROM VIEW DATATYPE FROM VIEW LIST OF COLS FROM CURSOR DATATYPE FROM CURSOR
---------------------- ------------------ ------------------------ --------------------
BUILDING_ID NUMBER(10) BUILDING_ID NUMBER(10)
LOCAL_ID VARCHAR(30) CAMPUS_ID NUMBER(10)
INSTITUTION_ID NUMBER(10) DELETE_FLAG CHAR(1)
EFFECTIVE_DATE TIMESTAMP(6) max(EFFECTIVE_DATE) TIMESTAMP(6)
END_DATE TIMESTAMP(6) END_DATE TIMESTAMP(6)
DELETE_FLAG CHAR(1) IMAGE_URL VARCHAR(500)
CAMPUS_ID NUMBER(10) INSTITUTION_ID NUMBER(10)
LOCATION_ID NUMBER(10) LOCAL_ID VARCHAR(30)
IMAGE_URL VARCHAR(500) LOCATION_ID NUMBER(10)
NAME VARCHAR(255) NAME VARCHAR(255)
They are not the same, yet by using the V_BUILDING%ROWTYPE in your sp_delete_building procedure, you're treating the ref cursor results as if the column order is the same as that of the view.
You can see that there are several mismatches between the datatypes of the view and the cursor select lists - it's probably the "LOCATION_ID/LOCAL_ID" mismatch that's causing the invalid number error that you're seeing.
You either need to change the order of your ref cursor so that the list of columns is returned in the same order as that of the view, or to explicitly list the columns of the cursor in the v_building_rec record type.
As an aside, you should give your max(EFFECTIVE_DATE) column in the refcursor an alias.

Insert data into a table from anothe table in Oracle

I have two equal Oracle tables which have same column names.
1) DoctorProfile_New
2) DoctorProfile_Old
Both tables share the same structure as follows.
DOCREGNO NOT NULL VARCHAR2(10)
DOCCATOGARY NOT NULL VARCHAR2(10)
ANAME1 VARCHAR2(50)
ANAME2 VARCHAR2(50)
ANAME3 VARCHAR2(50)
ANAME4 VARCHAR2(50)
ANAME5 VARCHAR2(50)
ANAME6 VARCHAR2(50)
ANAME7 VARCHAR2(50)
ANAME8 VARCHAR2(50)
ANAME9 VARCHAR2(50)
AGENDER VARCHAR2(2)
ARESCTOWN VARCHAR2(100)
DOCRCODE NUMBER
What I need to do is insert all the data from table DoctorProfile_Old to DoctorProfile_New.
The only difference is for the column DOCREGNO, I have to insert new hard coded value.
insert into DoctorProfile_New
(
DOCREGNO,
DOCCATOGARY,
ANAME1,
ANAME2,
ANAME3,
ANAME4,
ANAME5,
ANAME6,
ANAME7,
ANAME8,
ANAME9,
AGENDER,
ARESCTOWN
)
values
('000081',
(
select
DOCCATOGARY,
ANAME1,
ANAME2,
ANAME3,
ANAME4,
ANAME5,
ANAME6,
ANAME7,
ANAME8,
ANAME9,
AGENDER,
ARESCTOWN
from DoctorProfile_Old WHERE DOCREGNO='T07004'
)
)
I tried the above query and it always gives the Oracle error
ORA-00947: not enough values
You can use insert select to do this;
insert into DoctorProfile_New (
DOCREGNO,
DOCCATOGARY,
ANAME1,
ANAME2,
ANAME3,
ANAME4,
ANAME5,
ANAME6,
ANAME7,
ANAME8,
ANAME9,
AGENDER,
ARESCTOWN
)
select
'000081',
DOCCATOGARY,
ANAME1,
ANAME2,
ANAME3,
ANAME4,
ANAME5,
ANAME6,
ANAME7,
ANAME8,
ANAME9,
AGENDER,
ARESCTOWN
from DoctorProfile_Old
WHERE DOCREGNO='T07004';
How about this?
insert into DoctorProfile_New
(DOCREGNO,DOCCATOGARY,ANAME1,ANAME2,ANAME3,ANAME4,ANAME5,ANAME6,ANAME7,ANAME8,ANAME9,AGENDER,ARESCTOWN)
select '000081' as DOCREGNO, DOCCATOGARY,ANAME1,ANAME2,ANAME3,ANAME4,ANAME5,ANAME6,ANAME7,ANAME8,ANAME9,AGENDER,ARESCTOWN from DoctorProfile_Old WHERE DOCREGNO='T07004';
If you want to insert DOCREGNO automatically instead of hard coated value, you should create trigger and use sequence value.
CREATE OR REPLACE TRIGGER DoctorProfile_New_TRG
BEFORE INSERT OR UPDATE ON DoctorProfile_New
REFERENCING OLD AS OLD NEW AS NEW
FOR EACH ROW
BEGIN
if :new.DOCREGNO is null then
select lpad(some_sequence.nextval, 6, '0') into :new.DOCREGNO from dual;
end if;
END;
Insert all data from DoctorProfile_Old
insert into DoctorProfile_New
(DOCREGNO,DOCCATOGARY,ANAME1,ANAME2,ANAME3,ANAME4,ANAME5,ANAME6,ANAME7,ANAME8,ANAME9,AGENDER,ARESCTOWN)
select null,DOCCATOGARY,ANAME1,ANAME2,ANAME3,ANAME4,ANAME5,ANAME6,ANAME7,ANAME8,ANAME9,AGENDER,ARESCTOWN from DoctorProfile_Old;

Oracle Inner Join with Collection?

I want to join collection of table type with other tables in following example -
I have a function F_GetPendingFeeds which returns the table collection of type feed_log.
I want to join this returning collection with one of the table -
CREATE OR REPLACE PACKAGE BODY L_DemoPkg
IS
TYPE t_feedLog IS TABLE OF feed_log%ROWTYPE
INDEX BY PLS_INTEGER;
FUNCTION F_GetPendingFeeds
RETURN t_feedLog
IS
lo_feedLog t_feedLog;
BEGIN
SELECT feed_log_seq
, processed_dt
, processed_by
, create_dt
, created_by
BULK COLLECT INTO lo_feedLog
FROM feed_log
WHERE status_cd = 0;
RETURN lo_feedLog;
EXCEPTION
WHEN OTHERS THEN
--TODO: Log Exception
RAISE;
END F_GetPendingFeeds;
PROCEDURE P_ProcessFeed
IS
o_pendingFeed t_feedLog;
ln_totalRecords t_feedLog;
BEGIN
-- Get the list of pending feed ids
o_pendingFeed := F_GetPendingFeeds();
-- Check if new data is present for processing
IF o_pendingFeed.COUNT = 0 THEN
dbms_output.put_line('Feed processing failed. No data found.');
RETURN;
END IF;
SELECT COUNT(*)
INTO ln_totalRecords
FROM feed_details t1
, table(o_pendingFeed) t2 --ERROR: ORA-22905: cannot access rows from a non-nested table item
WHERE t1.feed_log_seq = t2.feed_log_seq;
EXCEPTION
WHEN OTHERS THEN
--TODO: Log Exception
RAISE;
END P_ProcessFeed;
END;
I am receiving error as -
PL/SQL: SQL Statement ignored
PL/SQL: ORA-22905: cannot access rows from a non-nested table
item
Please notice that I want to join collection with table -
FROM feed_details t1
, table(o_pendingFeed) t2 --ERROR: ORA-22905: cannot access rows from a non-nested table item WHERE t1.feed_log_seq = t2.feed_log_seq;
Prior to Oracle 12C you could only select from collections that have been created on the server using CREATE TYPE e.g.
SQL> CREATE TYPE r_feedLog IS OBJECT (foo NUMBER, bar VARCHAR2(20));
SQL> CREATE TYPE t_feedLog IS TABLE OF r_feedLog;
Then remove the declaration of t_feedLog from your package.
With Oracle 12C it is possible to select from PL/SQL tables defined in a package specification.
you have several errors here. Firstly, to access an array in a TABLE cast you need to use a SQL array (well, you could use a PL/SQL table still, but that only works for a pipelined function, as Oracle will create the SQL types silently for you; but even in that case its still neater to use a SQL array). so you'd need to do:
SQL> create type r_feedlog is object
2 (
3 feed_log_seq number,
4 processed_dt date,
5 processed_by varchar2(10),
6 create_dt date,
7 created_by varchar2(10)
8 );
9 /
Type created.
SQL> create type t_feedLog as table of r_feedlog;
2 /
Type created.
and then use that and NOT a pl/sql index-by table. secondly
ln_totalRecords t_feedLog;
should be a number not a collection as your selecting count(*) into it. Also :
BULK COLLECT INTO lo_transferFeedDef
should be
BULK COLLECT INTO lo_feedLog
you could have the function as pipelined of course i.e. something like:
CREATE OR REPLACE PACKAGE L_DemoPkg
as
type r_feedlog is record(feed_log_seq number,
processed_dt date,
processed_by varchar2(10),
create_dt date,
created_by varchar2(10));
type t_feedLog is table of r_feedlog;
function F_GetPendingFeeds return t_feedLog pipelined;
procedure P_ProcessFeed;
end;
/
and within the package body:
FUNCTION F_GetPendingFeeds
RETURN t_feedLog pipelined
IS
lo_feedLog r_feedlog;
BEGIN
for r_row in (SELECT feed_log_seq
, processed_dt
, processed_by
, create_dt
, created_by
FROM feed_log
WHERE status_cd = 0)
loop
lo_feedLog.feed_log_seq := r_row.feed_log_seq;
lo_feedLog.processed_dt := r_row.processed_dt;
lo_feedLog.processed_by := r_row.processed_by;
lo_feedLog.create_dt := r_row.create_dt;
lo_feedLog.created_by := r_row.created_by;
pipe row(lo_feedLog);
end loop;
END F_GetPendingFeeds;
within the procedure you could then just:
SELECT COUNT(*)
INTO ln_totalRecords
FROM feed_details t1
, table(F_GetPendingFeeds()) t2
WHERE t1.feed_log_seq = t2.feed_log_seq;
The above was keeping a pl/sql array. if you had the SQL array, the function would be a bit more compact:
FUNCTION F_GetPendingFeeds
RETURN t_feedLog pipelined
IS
BEGIN
for r_row in (SELECT r_feedlog(feed_log_seq
, processed_dt
, processed_by
, create_dt
, created_by) data
FROM feed_log
WHERE status_cd = 0)
loop
pipe row(r_row.data);
end loop;
END F_GetPendingFeeds;

How to programmatically set table name in PL/SQL?

I created the following simple PL/SQL stored procedure example to ask a specific question. This procedure inserts an employee name and id number into a table called employees_???. The ??? is explained below.
PROCEDURE hire_employee (emp_id IN INTEGER, name IN VARCHAR2, country IN VARCHAR2)
AS
BEGIN
INSERT INTO employees_??? VALUES (emp_id, name, 1000);
END hire_employee;
What I need is to set the table name based on the IN variable country. For example,
If country = 'usa', I want the INSERT line to read:
INSERT INTO employees_usa VALUES (emp_id, name, 1000);
If country = 'germany', I want the INSERT line to read:
INSERT INTO employees_germany VALUES (emp_id, name, 1000);
If country = 'france', I want the INSERT line to read:
INSERT INTO employees_france VALUES (emp_id, name, 1000);
etc...
Is there a way to do this in PL/SQL by substituting something in place of employee_??? so only one line of code for INSERT is used? Or is using a case or if/then/else statement the best way?
To answer your question, you have to use execute immediate and create your statement dynamically.
create or replace procedure hire_employee (
emp_id IN INTEGER
, name IN VARCHAR2
, country IN VARCHAR2 ) is
-- maximum length of an object name in Oracle is 30
l_table_name varchar2(30) := 'employees_' || country;
begin
execute immediate 'insert into ' || l_table_name
|| ' values (:1, :2, 1000)'
using emp_id, name;
end hire_employee;
However, this is a massively over-complicated way of storing the data. If you want to select all data you have to union large numbers of tables.
It would be far better to normalise the database properly and add country to an employees table.
Something like the following:
create table employees (
emp_id number(16)
, country varchar2(3) -- ISO codes
, name varchar2(4000) -- maximum who knows what name people might have
, < other_columns >
, constraint pk_employees primary key ( emp_id )
);
Your procedure then becomes a very simple insert statement:
create or replace procedure hire_employee (
emp_id in integer
, name in varchar2
, country in varchar2 ) is
insert into employees
values ( emp_id, country, name, 1000 );
end hire_employee;
You can use dynamic SQL and the EXECUTE IMMEDIATE construct. In this, you construct the query as a string and then execute it. A good example is at http://docs.oracle.com/cd/B10500_01/appdev.920/a96590/adg09dyn.htm

Resources