How to programmatically set table name in PL/SQL? - oracle

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

Related

SYS_REFCURSOR is returning all the rows from table without considering the IN parameter

I am facing a weird problem here.
PROCEDURE USL_EMPLOYEEBYID (
EMPLOYEE_ID IN NUMBER,
EMPIDCURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN EMPIDCURSOR FOR
SELECT emp.employee_id,emp.employee_name,emp.present_address,emp.permanent_address,emp.status
FROM Employee_Info emp
WHERE emp.employee_id = EMPLOYEE_ID;
END;
This procedure should give me a single employee upon entering the employee Id. But it is returning all the employees.
What am I doing wrong here?
In your query, Oracle interprets EMPLOYEE_ID as the column EMPLOYEE_ID, not the input parameter, here you find something more; in this way, your where condition is something like a=a.
Change the parameter name to distinguish it from the table column:
PROCEDURE USL_EMPLOYEEBYID (
p_EMPLOYEE_ID IN NUMBER,
po_EMPIDCURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN po_EMPIDCURSOR FOR
SELECT emp.employee_id,emp.employee_name,emp.present_address,emp.permanent_address,emp.status
FROM Employee_Info emp
WHERE emp.employee_id = p_EMPLOYEE_ID;
END;
this is a good practice, to always know in your code whether you are handling an input parameter, a local variable, a column and so on

pl sql insert into within a procedure and dynamic variables

I need some help with PL SQL. I have to insert some data into table. Another application is calling my procedure and caller is passing few details which I also need to insert into my table.
here is the syntax I am struggling with:
PROCEDURE invform_last2orders_item_insert( p_userId IN NUMBER
,p_accountId IN NUMBER
,p_site_Id IN NUMBER
,p_return_message OUT VARCHAR2) IS
Begin
insert into mytable
(p_userId , p_accountId , p_site_Id , sku, description, 'Cart', 1, unitId)
as
select sku, description, unitId
from mycatalogtable where site_id= p_site_Id ) ;
End;
Can you help me with syntax? I need to pass three parameters from called in parameter and some values returned from select query. How can I achieve this?
thank you for your help.
That would be something like this; see comments within code:
PROCEDURE invform_last2orders_item_insert
( p_userId IN NUMBER
,p_accountId IN NUMBER
,p_site_Id IN NUMBER
,p_return_message OUT VARCHAR2)
IS
Begin
insert into mytable
-- first name all columns you'll be inserting into; I don't know their
-- names so I just guessed
(userid,
accountid,
siteid,
sku,
description,
col1,
col2,
unitid
)
-- if you were to insert only values you got via parameters, you'd use the
-- VALUE keyword and insert those values separately.
-- As some of them belong to a table, use SELECT statement
(select p_userid,
p_accountid,
p_siteid,
c.sku,
c.description,
'Cart',
1,
c.unitid
from mycatalogtable c
where c.site_id = p_site_Id
);
-- I don't know what you are supposed to return; this is just an example
p_return_message := sql%rowcount || ' row(s) inserted';
End;
in your select statement you should have the same number of columns as you are inserting into the table, your code should be something like this example,
DECLARE
userid varchar2(20) := 'Jack';
Begin
INSERT INTO mytable (SELECT userid, SPORT from OLYM.OLYM_SPORTS);
commit;
end;

i need make an INSERT INTO tablex (SELECT columns)VALUES(v1...vn)

I need to make an "insert dynamically" random data in table WORKER, now I have the fieldname, the datatype and the size of every column of this table WORKER in other table TEMPORAL (this info was captured before and saved on this table).
The question is: when I make the insert, I need to know the fields from this table to make the insert. How do I do it? How do I take the values from TEMPORAL to make the insert? Until now I got this but doesn't work:
insert into worker(select chr(500)||upper(name)||',' from temporal)
I thought about this too:
select chr(500)||upper(name)||',' from temporal
or make a dynamic statement using this values and a cursor.
thx to everibody.
TEMPORAL contains the name, the datatype and the length of the table TRABAJADOR, captured for do the insertion of random data. but i most to do this dynamically for that reason i don't know what or how much columns this table had(i have to do this for every or any table on any shema). this is what temporal contains:
NAME Typ Length
ID_MAN NUMBER 22
SALARIO NUMBER 22
GENERO VARCHAR2 8
FDN DATE 7
NOMBRE VARCHAR2 100
DIRECCION VARCHAR2 60
DEPT VARCHAR2 60
PAIS VARCHAR2 60
CATEGORIA VARCHAR2 60
i got right now in one variable(COL) the names of the columns for pass into the insert statement
insert into worker(col). but i dont know how to do this or if even is posible. THX
You can achieve that with dynamic SQL. This should work:
declare
myQuery varchar2(4000);
begin
-- Open insert and aggregate all values
select 'insert into worker('
|| listagg(t.name, ', ') within group (order by t.name)
into myQuery
from TEMPORAL t;
-- Close insert
myQuery := myQuery || ') values(<here you have to specify your values>)';
execute immediate myQuery;
end;
/

select inside loop oracle

I have written a stored procedure with a query inside a loop.
This query sets the records into a custom data type of the type RECORD something like
TYPE finalrecord
IS
RECORD
(
corh VARCHAR2(1),
myspissueid NUMBER(10),
mypkey VARCHAR2(10),
mycreated DATE,
myprevstepname VARCHAR2(10),
mystepname VARCHAR2(10),
mystorypoints NUMBER(2) );
myfinalrecord finalrecord;
The for loop goes like
for vh in (select * from table1 where abc=3)
loop
select steps.current_or_history,
steps.issueid,
steps.pkey,
steps.created,
steps.prev_step_name,
steps.step_name,
steps.story_points
from steps where column1 = 'xyz' and column2=vh.column2;
end loop;
Every time the inner loop is executed, the SELECT statement would return more than one record. I want to add this record to a main variable (as a collection..but varray or nested table or associative array) and return that variable as a output of the stored procedure.
Any idea?
declare
type t is table of finalrecord;
my_table t;
begin
for vh in (select * from table1 where abc = 3) loop
execute immediate 'select finalrecord(steps.current_or_history,
steps.issueid,
steps.pkey,
steps.created,
steps.prev_step_name,
steps.step_name,
steps.story_points)
from steps where column1 = ''xyz'' and column2=vh.column2' bulk
collect
into my_table;
end loop;
end;
you can try this if it works you can also create procedure...

How do I return a table using a procedure that has a cursor in it ? PL/SQL

I have a procedure that is given one value as input, after doing some process, using a cursor within that procedure, i want the procedure to return a table.
this one value that is given as an input_param is a non unique ID that is used to identify multiple rows.
so that way I can run a command like :
select * from {call(procedure_name(input_Param)}
My knowledge of PLSQL is limited.
I'm not sure if a procedure can have a a cursor definition inside it, and if that is possible then how do i return an entire Table from the procedure.
BTW: This procedure has to be called using a select statement, and if not a select statement then it should return a table, with a select * at the end.
If I have to specify whih columns to output instead of select * do I need to provide all those column names as input_Params ? ie. If I want the procedure to return only a small number of columns what do I do?
Thanks
You need to use PIPELINED function. Example below, link to more information at the end.
CREATE TABLE test_pipe (
id NUMBER,
name VARCHAR2(20),
salary NUMBER
);
INSERT INTO test_pipe VALUES (1, 'Smith', 5000);
INSERT INTO test_pipe VALUES (2, 'Brown', 8000);
INSERT INTO test_pipe VALUES (3, 'Bay', 10000);
COMMIT;
CREATE TYPE t_pipe_row_test AS OBJECT (
name VARCHAR2(20),
salary NUMBER
);
/
CREATE TYPE t_pipe_test_tab IS TABLE OF t_pipe_row_test;
/
CREATE OR REPLACE FUNCTION test_func_pipe(p_min_salary IN NUMBER)
RETURN t_pipe_test_tab
PIPELINED
AS
BEGIN
FOR v_rec IN (SELECT name, salary
FROM test_pipe
WHERE salary >= p_min_salary)
LOOP
PIPE ROW (t_pipe_row_test(v_rec.name, v_rec.salary));
END LOOP;
END;
/
SELECT * FROM TABLE(test_func_pipe(6000));
Output:
NAME SALARY
-------------------- ----------
Brown 8000
Bay 10000
More about pipelined functions by Tim Hall

Resources