oracle - multiple insert into type table collection - oracle

I have created the following object in oracle 11g.
CREATE OR REPLACE TYPE myObject as object(
fieldOne number,
fieldTwo number
);
And created a new table type of myObject;
CREATE OR REPLACE TYPE myTable IS TABLE OF myObject;
I would now like to create a new instance of myTable and add several hard-coded rows to myTable on the SQL Plus command line then pass the object to myProcedure as a parameter.
I have tried the following;
declare newTable myTable;
begin
select myObject(50,5) bulk collect into newTable from dual;
select myObject(40,7) bulk collect into newTable from dual;
myProcedure(newTable);
commit;
end;
Which sort-of works although the second select into statement overwrites the first.
My question is; how can I add multiple rows to newTable?
Many Thanks in Advance :)

declare
newTable myTable;
begin
newTable := myTable();
newTable.extend(2); -- The desired size of the collection
-- Oracle collections begin at index 1, not 0
newTable(1) := myObject(50, 5);
newTable(2) := myObject(40, 7);
myProcedure(newTable);
end;

Related

Result of Stored Procedure save in table variable

I have question: I have stored procedure A(return three out paramters). I want to call procedure A in loop and insert these three parameters into temporaty table and return this table.
DECLARE
Type TestTable IS TABE OF NUMBER; -- for example one parameter!!!
myTable TestTable;
BEGIN
LOOP
A(o_param1, o_param2, o_param3);
-- myTable insert o_param1,2,3;
-- insert into myTable values(99); - here I have error PL/SQL: ORA-00942: table or view does not exist
END LOOP;
SELECT * FROM myTable;
END;
I dont know how to do -- myTable insert o_param1,2,3;. Please help me.
write insert statement inside the loop. so for each loop you can have the values inserted to the table and give a commit after the loop.
But you cannot have a select * from table inside the anonymous block. Remove that from the block and after end; you can try running select * from table to see the output.
BEGIN
LOOP
A(o_param1, o_param2, o_param3);
-- myTable insert o_param1,2,3;
insert into myTable values (o_param1, o_param2, o_param3);
END LOOP;
commit;
--SELECT * FROM myTable;-
END;
SELECT * FROM myTable;
> Blockquote
First, you cannot insert data into myTable directly (insert into myTable) because Oracle table types, declared in a declare section of the script, are not visible in sql statements (exception - insert using 'bulk collect' with types, declared in Oracle dictionary).
Even if you insert data in myTable using myTable(idx)... you can not select it outside the script because myTable exists only inside the script.
I think the simpliest way is to create usual table or global temporary table. If you will use global temporary table create it with 'ON COMMIT PRESERVE ROWS' (if you use commit after insert)

Using %ROWTYPE in Procedure in Oracle

I am trying to use %ROWTYPE in my code and trying to insert value into it using a cursor for loop as below :
CREATE or REPLACE PROCEDURE test_acr (
PROJECT_START_DATE IN DATE,USER_ID IN VARCHAR2)
IS TYPE acr_new IS TABLE OF acr_projected_new%ROWTYPE
INDEX BY SIMPLE_INTEGER;
acr_projected_neww acr_new;
CURSOR WEEKENDING_DATE IS
SELECT WEEKEND_DATE
FROM weekending_table
WHERE WEEKEND_DATE BETWEEN PROJECT_START_DATE AND sysdate;
BEGIN
FOR WEEKEND_DATE_REC in WEEKENDING_DATE LOOP
INSERT INTO acr_projected_neww(WEEKEND_DATE,USERID,TIMESTAMP,ACR_PROJECTED,artificial_id)
SELECT WEEKEND_DATE_REC.WEEKEND_DATE,USER_ID,sysdate,
(select sum(acr_h.activity_impact)
FROM ACR_HISTORY acr_h
LEFT JOIN Activity act on act.activity_id = acr_h.activity_id
LEFT JOIN Activity_Date_Duration act_d on act_d.activity_id = act.activity_id),1 from dual;
END LOOP;
END test_acr;
When i try to run this i get below error:
Error(54,14): PL/SQL: ORA-00942: table or view does not exist
My Requirement is to create virtual table and insert the data into it using cursor for loop if not then any other means is appreciated.
Please help it will be greatly appreciated!
Looks like table name is incorrect in your INSERT statement.
You need not use two queries. Instead, define your cursor such that it has all the columns of the records you want to store in the collection. Then use BULK COLLECT INTO instead of insert as shown. Define your collection as table of cursor%ROWTYPE.
CREATE OR REPLACE PROCEDURE test_acr
IS
CURSOR WEEKENDING_DATE
IS
SELECT a.col1,a.col2,b.col1,b.col2 ,c.col1
from table1 a , table2 b LEFT JOIN table3 c; --Here include all the data from the required tables.
TYPE acr_new
IS
TABLE OF WEEKENDING_DATE%ROWTYPE;
acr_projected_neww acr_new;
BEGIN
FETCH WEEKENDING_DATE BULK COLLECT INTO acr_projected_neww;
END test_acr;
If you need to manipulate your data (access each row - then see script below, this is sequential access (inserts) into nested table (PL/SQL collection)
CREATE or REPLACE PROCEDURE test_acr (PROJECT_START_DATE IN DATE,USER_ID IN VARCHAR2)
IS
TYPE acr_new
IS TABLE OF acr_projected_new%ROWTYPE; // nested table, notice absence of INDEX by clause
acr_projected_neww acr_new := acr_projected_neww(); // instantiation, constructor call
CURSOR WEEKENDING_DATE IS
SELECT WEEKEND_DATE
FROM weekending_table
WHERE WEEKEND_DATE BETWEEN PROJECT_START_DATE AND sysdate;
BEGIN
FOR WEEKEND_DATE_REC in WEEKENDING_DATE
LOOP
acr_new.extend; // make room for the next element in collection
acr_new(acr_new.last) := WEEKEND_DATE_REC; // Adding seq. to the end of collection
...
END LOOP;
END test_acr;
However if you want to BULK INSERT (there is no requirement to get access to each row) see script below
CREATE or REPLACE PROCEDURE test_acr (PROJECT_START_DATE IN DATE,USER_ID IN VARCHAR2)
IS
TYPE acr_new IS TABLE OF acr_projected_new%ROWTYPE; // no INDEX BY clause
acr_projected_neww acr_new = acr_new(); // Notice constructor call
CURSOR WEEKENDING_DATE IS
SELECT WEEKEND_DATE
FROM weekending_table
WHERE WEEKEND_DATE BETWEEN PROJECT_START_DATE AND sysdate;
BEGIN
FETCH WEEKENDING_DATE BULK COLLECT INTO acr_projected_neww;
...
END LOOP;
END test_acr;
I have used temporary table outside my procedure:
CREATE GLOBAL TEMPORARY TABLE "MY_TEMP"
( "WEEKEND_DATE" DATE,
"USERID" VARCHAR2(255 BYTE),
"TIMESTAMP" TIMESTAMP (6),
"ACR_PROJECTED" NUMBER,
"ARTIFICIAL_ID" NUMBER
) ON COMMIT PRESERVE ROWS ;
i have just used the above temporary table inside my Procedure
create or replace PROCEDURE GET_ACR_TEST(
PROJECT_START_DATE IN DATE ,
USER_ID IN VARCHAR2,
) AS
CURSOR WEEKENDING_DATE IS
SELECT WEEKEND_DATE, DURATION
FROM weekending_table where WEEKEND_DATE between PROJECT_START_DATE and sysdate;
Begin
FOR WEEKEND_DATE_REC in WEEKENDING_DATE
LOOP
insert into MY_TEMP (WEEKEND_DATE,USERID,TIMESTAMP,ACR_PROJECTED,artificial_id)
select WEEKEND_DATE_REC.WEEKEND_DATE,USER_ID,sysdate,
(select sum(acr_h.activity_impact)
from ACR_HISTORY acr_h
LEFT JOIN Activity act on act.activity_id = acr_h.activity_id
LEFT JOIN Activity_Date_Duration act_d on act_d.activity_id = act.activity_id),1
from dual;
End Loop;
END GET_ACR_TEST;
The above method is working.
Thank you all for your comments!

Declare Table Variable in Oracle Procedure

I'm having a heck of a time trying to find an example of this being done. I have a procedure, and as part of that procedure I want to store the results of a SELECT statement so that I can work against that set, and then use it as a reference to update the original records when it's all done.
The difficulty I'm having is in declaring the temporary table variable. Here's an example of what I'm trying to do:
PROCEDURE my_procedure
IS
output_text clob;
temp_table IS TABLE OF MY_TABLE%ROWTYPE; -- Error on this line
BEGIN
SELECT * BULK COLLECT INTO temp_table FROM MY_TABLE WHERE SOME_DATE IS NULL;
-- Correlate results into the clob for sending to email (working)
-- Set the SOME_DATE value of the original record set where record is in temp_table
I get an error on the second occurrence of IS, saying that it is an unexpected symbol. This suggests to me that my table variable declaration is either wrong, or in the wrong place. I've tried putting it into a DECLARE block after BEGIN, but I just get another error.
Where should this declaration go? Alternatively, if there is a better solution I'll take that too!
CREATE OR REPLACE PROCEDURE PROCEDURE1 AS
output_text clob;
type temp_table_type IS TABLE OF MY_TABLE%ROWTYPE;
temp_table temp_table_type;
BEGIN
SELECT * BULK COLLECT INTO temp_table FROM MY_TABLE;
END PROCEDURE1;
or
CREATE OR REPLACE PROCEDURE PROCEDURE1 ( output_text OUT clob ) IS
type temp_table_type IS TABLE OF MY_TABLE%ROWTYPE
INDEX BY BINARY_INTEGER;
temp_table temp_table_type;
BEGIN
SELECT * BULK COLLECT INTO temp_table FROM MY_TABLE;
FOR indx IN 1 .. temp_table.COUNT
LOOP
something := temp_table(indx).col_name;
END LOOP;
END PROCEDURE1;
I had a similiar problem and found this:
Selecting Values from Oracle Table Variable / Array?
The global temporary table can be used like a regular table, but its content is only temporary (deleted at end of session/transaction) and each session has its own table content.
If you don't need dynamic SQL this can be used as good solution:
CREATE GLOBAL TEMPORARY TABLE temp_table
(
column1 NUMBER,
column2 NUMBER
)
ON COMMIT DELETE ROWS;
PROCEDURE my_procedure
IS
output_text clob;
BEGIN
-- Clear temporary table for this session (to be sure)
DELETE FROM temp_table;
-- Insert data into temporary table (only for this session)
INSERT INTO temp_table SELECT * FROM MY_TABLE WHERE SOME_DATE IS NULL;
-- ...
END;
The only disadvantages are, in my opinion, that you got another table and that the temporary table is not dynamic.

How do you select into a nested type in oracle pl/sql?

I want to be able to delete by rowid then immediately insert the data being deleted in an audit table.
There are far too many records to
INSERT INTO ... SELECT CRITERIA then DELETE ... CRITERIA.
I already know how to do everything just using rowid and INSERT INTO ... SELECT.
Inside package body:
TYPE some_type IS RECORD (
row_id ROWID,
full_row table_name%ROWTYPE
);
TYPE some_type_list IS TABLE OF some_type
INDEX BY BINARY_INTEGER;
PROCEDURE do_stuff
IS
lc_data SYS_REFCURSOR;
lt_recs some_type_list;
BEGIN
OPEN lc_date FOR
SELECT rowid, a.*
FROM table_name;
LOOP
FETCH lc_data
BULK COLLECT INTO lt_recs
LIMIT 50000;
EXIT WHEN lt_recs.COUNT = 0;
--
FORALL i IN lt_recs.FIRST..lt_recs.LAST
DELETE table_name
WHERE ROWID = lt_recs(i).row_id;
--
FORALL i IN lt_recs.FIRST..lt_recs.LAST
INSERT INTO table_name_audit VALUES lt_recs(i).full_row;
END LOOP;
END;
If I try that i get the following error:
Line: 117 Column: 25 Type: error Text: PLS-00597: expression 'LT_RECS' in the INTO list is of wrong type
Oracle versions prior to 11gR2 restrict us to use BULK COLLECT into a collection (nested table or varray) of records. Read more here on Oracle Docs.
If you want to see how it is done in 11gR2, scroll down to EDIT 2 section of this answer.
An alternative tho this can be the use of separate collections for every column- an approach that is most widely used. In this you can have:
/*
TYPE some_type IS RECORD (
row_id ROWID,
full_row table_name%ROWTYPE
);
TYPE some_type_list IS TABLE OF some_type
INDEX BY BINARY_INTEGER;
-- */
CREATE TYPE t_row_id IS TABLE OF ROWID;
CREATE TYPE t_col1 IS TABLE OF table_name.col1%TYPE;
CREATE TYPE t_col2 IS TABLE OF table_name.col2%TYPE;
CREATE TYPE t_col3 IS TABLE OF table_name.col3%TYPE;
...
...
CREATE TYPE t_colN IS TABLE OF table_name.colN%TYPE;
PROCEDURE do_stuff
IS
lc_data SYS_REFCURSOR;
-- lt_recs some_type_list;
row_id t_row_id;
col1 t_col1;
col2 t_col2;
col3 t_col3;
...
...
colN t_colN;
BEGIN
OPEN lc_date FOR
SELECT rowid, a.*
FROM table_name;
LOOP
FETCH lc_data
BULK COLLECT INTO row_id, col1, col2, col3, ..., colN
LIMIT 50000;
EXIT WHEN lt_recs.COUNT = 0;
--
FORALL i IN row_id.FIRST..row_id.LAST
DELETE table_name
WHERE ROWID = row_id(i);
--
FORALL i IN col1.FIRST..col1.LAST
INSERT INTO table_name_audit VALUES (col1(i), col2(i), col3(i), ..., colN(i));
END LOOP;
END;
I have not removed many of the rows in your program in order to let you understand the changes.
EDIT : Refer to the "Restrictions on BULK COLLECT" section of the Oracle Docs link I have given above and also here.
EDIT #2 :
You have to use CREATE TYPE ... IS OBJECT instead of RECORD. Also, You need to modify the SELECT statement the way I have done when I tried it. Please see the Oracle Docs here and a StackOverflow question here for further reference.
The code I tried on my machine (runs Oracle 11g R2) is as follows:
-- SELECT * FROM user_objects WHERE object_type = 'TYPE';
CLEAR SCREEN;
SET SERVEROUTPUT ON;
CREATE OR REPLACE TYPE temp_t_test AS OBJECT ( -- << OBJECT, not RECORD.
test_id INTEGER
, test_val VARCHAR2(50)
);
/
CREATE OR REPLACE TYPE temp_tbl_test AS TABLE OF TEMP_T_TEST;
/
DECLARE
v_test TEMP_TBL_TEST;
BEGIN
SELECT temp_t_test(t_id, t_val) -- << Notice the syntax
-- I'm selecting the columns as the defined OBJECT type.
BULK COLLECT INTO v_test
FROM (SELECT 1 AS t_id, 'ABCD' AS t_val FROM dual
UNION ALL
SELECT 2, 'WXYZ' FROM dual
UNION ALL
SELECT 3, 'PQRS' FROM dual);
dbms_output.put_line('Bulk Collect Successful!');
END;
/
** OUTPUT **:
TYPE temp_t_test compiled
TYPE temp_tbl_test compiled
anonymous block completed
Bulk Collect Successful!
I don't think that I'd take this approach at all, to be honest.
A faster method would be along the lines of performing a multitable insert:
insert the table columns into the audit table, possibly using direct path (APPEND hint) for efficiency
insert the rowid's into an on-commit-delete-rows global temporary table.
Then perform a delete against the original table using DELETE .. WHERE ROWID IN (SELECT ORIGINAL_ROWID FROM MY_GLOBAL_TEMP_TAB)
... and then commit.
Faster, and less code I think.
What you're trying to works in 11gR2 - what version are you on?.
The only wrong-looking thing in your post is this:
OPEN lc_date FOR
SELECT rowid, a.*
FROM table_name;
It ought to be this ...
OPEN lc_data FOR
SELECT a.rowid, a.*
FROM table_name a;
... but these may simply be typos you introduced when sanitizing your code to post here.

select from employee table where index is not in a collection

I have a procedure like below:
SET serveroutput ON
DECLARE
TYPE list_of_names_t
IS TABLE OF emp.emp_index%TYPE;
ignoreIndexes LIST_OF_NAMES_T := List_of_names_t();
BEGIN
-- Logic here which fills the values in the collection ignoreIndexes and considerIndexes
-- Line XX
END;
At Line XX, I want to add the below line
SELECT * FROM emp WHERE emp_index NOT IN ignoreIndexes But not able to get the correct syntax, How to make that possible?
If the collection type is declared at the schema level, it can be used in SQL statements or dynamically .So do the following steps to make this work .
CREATE OR REPLACE TYPE list_of_names AS OBJECT(emp_index NUMBER);
CREATE OR REPLACE TYPE list_of_names_t IS TABLE OF list_of_names;
Create your anonymous block as shown below:
SET serveroutput ON
DECLARE
ignoreIndexes LIST_OF_NAMES_T := List_of_names_t();
emp_cur SYS_REFCURSOR;
BEGIN
-- Populate the collection with some data
SELECT list_of_names(LEVEL)
BULK COLLECT INTO ignoreIndexes
FROM dual CONNECT BY LEVEL<21;
OPEN emp_cur FOR
SELECT *
FROM EMP e
WHERE NOT EXISTS(SELECT 1
FROM table(ignoreIndexes) tt
WHERE tt.emp_index = e.emp_index
);
END;

Resources